diff --git a/Cargo.toml b/Cargo.toml index 2bd30c808..b1943e06a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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] diff --git a/bellman/.gitignore b/bellman/.gitignore deleted file mode 100644 index a9d37c560..000000000 --- a/bellman/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -target -Cargo.lock diff --git a/bellman/COPYRIGHT b/bellman/COPYRIGHT deleted file mode 100644 index 8b5f8cf37..000000000 --- a/bellman/COPYRIGHT +++ /dev/null @@ -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. diff --git a/bellman/Cargo.toml b/bellman/Cargo.toml deleted file mode 100644 index a6f02b3a4..000000000 --- a/bellman/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -authors = ["Sean Bowe "] -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" } diff --git a/bellman/LICENSE-APACHE b/bellman/LICENSE-APACHE deleted file mode 100644 index 16fe87b06..000000000 --- a/bellman/LICENSE-APACHE +++ /dev/null @@ -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. diff --git a/bellman/LICENSE-MIT b/bellman/LICENSE-MIT deleted file mode 100644 index 31aa79387..000000000 --- a/bellman/LICENSE-MIT +++ /dev/null @@ -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. diff --git a/bellman/README.md b/bellman/README.md deleted file mode 100644 index 850f2e3e6..000000000 --- a/bellman/README.md +++ /dev/null @@ -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. diff --git a/bellman/src/domain.rs b/bellman/src/domain.rs deleted file mode 100644 index 91a8b8355..000000000 --- a/bellman/src/domain.rs +++ /dev/null @@ -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 2n 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> { - coeffs: Vec, - exp: u32, - omega: S, - omegainv: S, - geninv: S, - minv: S, -} - -impl> AsRef<[G]> for EvaluationDomain { - fn as_ref(&self) -> &[G] { - &self.coeffs - } -} - -impl> AsMut<[G]> for EvaluationDomain { - fn as_mut(&mut self) -> &mut [G] { - &mut self.coeffs - } -} - -impl> EvaluationDomain { - pub fn into_coeffs(self) -> Vec { - self.coeffs - } - - pub fn from_coeffs(mut coeffs: Vec) -> Result, 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>) { - 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) { - 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: 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(pub G); - -impl PartialEq for Point { - fn eq(&self, other: &Point) -> bool { - self.0 == other.0 - } -} - -impl Copy for Point {} - -impl Clone for Point { - fn clone(&self) -> Point { - *self - } -} - -impl Group for Point { - 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(pub S); - -impl PartialEq for Scalar { - fn eq(&self, other: &Scalar) -> bool { - self.0 == other.0 - } -} - -impl Copy for Scalar {} - -impl Clone for Scalar { - fn clone(&self) -> Scalar { - *self - } -} - -impl Group for Scalar { - 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>(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>(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>( - 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(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::random(rng))).collect(); - let mut b: Vec<_> = (0..coeffs_b).map(|_| Scalar::(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::(rng); -} - -#[cfg(feature = "pairing")] -#[test] -fn fft_composition() { - use bls12_381::Scalar as Fr; - use rand_core::RngCore; - - fn test_comp(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::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::(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(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::random(rng))) - .collect::>(); - 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::(rng); -} diff --git a/bellman/src/gadgets.rs b/bellman/src/gadgets.rs deleted file mode 100644 index b0ce73472..000000000 --- a/bellman/src/gadgets.rs +++ /dev/null @@ -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 { - fn get(&self) -> Result<&T, SynthesisError>; -} - -impl Assignment for Option { - fn get(&self) -> Result<&T, SynthesisError> { - match *self { - Some(ref v) => Ok(v), - None => Err(SynthesisError::AssignmentMissing), - } - } -} diff --git a/bellman/src/gadgets/blake2s.rs b/bellman/src/gadgets/blake2s.rs deleted file mode 100644 index f5e46eaae..000000000 --- a/bellman/src/gadgets/blake2s.rs +++ /dev/null @@ -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, M>( - mut cs: M, - v: &mut [UInt32], - a: usize, - b: usize, - c: usize, - d: usize, - x: &UInt32, - y: &UInt32, -) -> Result<(), SynthesisError> -where - M: ConstraintSystem>, -{ - 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>( - 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>( - mut cs: CS, - input: &[Boolean], - personalization: &[u8], -) -> Result, 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![]; - - 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::::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::::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::::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::::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 = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - - h.update(&data); - - let hash_result = h.finalize(); - - let mut cs = TestConstraintSystem::::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 = hex!("be9f9c485e670acce8b1516a378176161b20583637b6f1c536fbc1158a0a3296831df2920e57a442d5738f4be4dd6be89dd7913fc8b4d1c0a815646a4d674b77f7caf313bd880bf759fcac27037c48c2b2a20acd2fd5248e3be426c84a341c0a3c63eaf36e0d537d10b8db5c6e4c801832c41eb1a3ed602177acded8b4b803bd34339d99a18b71df399641cc8dfae2ad193fcd74b5913e704551777160d14c78f2e8d5c32716a8599c1080cb89a40ccd6ba596694a8b4a065d9f2d0667ef423ed2e418093caff884540858b4f4b62acd47edcea880523e1b1cda8eb225c128c2e9e83f14f6e7448c5733a195cac7d79a53dde5083172462c45b2f799e42af1c9").to_vec(); - assert_eq!(data.len(), 256); - - let mut cs = TestConstraintSystem::::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 = hex!("5dcfe8bab4c758d2eb1ddb7ef337583e0df3e2c358e1755b7cd303a658de9a1227eed1d1114179a5c3c38d692ff2cf2d4e5c92a9516de750106774bbf9f7d063f707f4c9b6a02c0a77e4feb99e036c3ccaee7d1a31cb144093aa074bc9da608f8ff30b39c3c60e4a243cc0bbd406d1262a7d6607b31c60275c6bcc8b0ac49a06a4b629a98693c5f7640f3bca45e4977cfabc5b17f52838af3433b1fd407dbbdc131e8e4bd58bcee85bbab4b57b656c6a2ec6cf852525bc8423675e2bf29159139cd5df99db94719f3f7167230e0d5bd76f6d7891b656732cef9c3c0d48a5fa3d7a879988157b39015a85451b25af0301ca5e759ac35fea79dca38c673ec6db9f3885d9103e2dcb3304bd3d59b0b1d01babc97ef8a74d91b6ab6bf50f29eb5adf7250a28fd85db37bff0133193635da69caeefc72979cf3bef1d2896d847eea7e8a81e0927893dbd010feb6fb845d0399007d9a148a0596d86cd8f4192631f975c560f4de8da5f712c161342063af3c11029d93d6df7ff46db48343499de9ec4786cac059c4025ef418c9fe40132428ff8b91259d71d1709ff066add84ae944b45a817f60b4c1bf719e39ae23e9b413469db2310793e9137cf38741e5dd2a3c138a566dbde1950c00071b20ac457b46ba9b0a7ebdddcc212bd228d2a4c4146a970e54158477247c27871af1564b176576e9fd43bf63740bf77434bc4ea3b1a4b430e1a11714bf43160145578a575c3f78ddeaa48de97f73460f26f8df2b5d63e31800100d16bc27160fea5ced5a977ef541cfe8dadc7b3991ed1c0d4f16a3076bbfed96ba3e155113e794987af8abb133f06feefabc2ac32eb4d4d4ba1541ca08b9e518d2e74b7f946b0cbd2663d58c689359b9a565821acc619011233d1011963fa302cde34fc9c5ba2e03eeb2512f547391e940d56218e22ae325f2dfa38d4bae35744ee707aa5dc9c17674025d15390a08f5c452343546ef6da0f7").to_vec(); - assert_eq!(data.len(), 700); - - let mut cs = TestConstraintSystem::::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 = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - - h.update(&data); - - let hash_result = h.finalize(); - - let mut cs = TestConstraintSystem::::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()); - } - } -} diff --git a/bellman/src/gadgets/boolean.rs b/bellman/src/gadgets/boolean.rs deleted file mode 100644 index e5e24b6b6..000000000 --- a/bellman/src/gadgets/boolean.rs +++ /dev/null @@ -1,1824 +0,0 @@ -//! Gadgets for allocating bits in the circuit and performing boolean logic. - -use ff::{BitIterator, PrimeField}; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; - -use super::Assignment; - -/// Represents a variable in the constraint system which is guaranteed -/// to be either zero or one. -#[derive(Clone)] -pub struct AllocatedBit { - variable: Variable, - value: Option, -} - -impl AllocatedBit { - pub fn get_value(&self) -> Option { - self.value - } - - pub fn get_variable(&self) -> Variable { - self.variable - } - - /// Allocate a variable in the constraint system which can only be a - /// boolean value. Further, constrain that the boolean is false - /// unless the condition is false. - pub fn alloc_conditionally( - mut cs: CS, - value: Option, - must_be_false: &AllocatedBit, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let var = cs.alloc( - || "boolean", - || { - if *value.get()? { - Ok(Scalar::one()) - } else { - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain: (1 - must_be_false - a) * a = 0 - // if must_be_false is true, the equation - // reduces to -a * a = 0, which implies a = 0. - // if must_be_false is false, the equation - // reduces to (1 - a) * a = 0, which is a - // traditional boolean constraint. - cs.enforce( - || "boolean constraint", - |lc| lc + CS::one() - must_be_false.variable - var, - |lc| lc + var, - |lc| lc, - ); - - Ok(AllocatedBit { - variable: var, - value, - }) - } - - /// Allocate a variable in the constraint system which can only be a - /// boolean value. - pub fn alloc(mut cs: CS, value: Option) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let var = cs.alloc( - || "boolean", - || { - if *value.get()? { - Ok(Scalar::one()) - } else { - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain: (1 - a) * a = 0 - // This constrains a to be either 0 or 1. - cs.enforce( - || "boolean constraint", - |lc| lc + CS::one() - var, - |lc| lc + var, - |lc| lc, - ); - - Ok(AllocatedBit { - variable: var, - value, - }) - } - - /// Performs an XOR operation over the two operands, returning - /// an `AllocatedBit`. - pub fn xor(mut cs: CS, a: &Self, b: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "xor result", - || { - if *a.value.get()? ^ *b.value.get()? { - result_value = Some(true); - - Ok(Scalar::one()) - } else { - result_value = Some(false); - - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain (a + a) * (b) = (a + b - c) - // Given that a and b are boolean constrained, if they - // are equal, the only solution for c is 0, and if they - // are different, the only solution for c is 1. - // - // ¬(a ∧ b) ∧ ¬(¬a ∧ ¬b) = c - // (1 - (a * b)) * (1 - ((1 - a) * (1 - b))) = c - // (1 - ab) * (1 - (1 - a - b + ab)) = c - // (1 - ab) * (a + b - ab) = c - // a + b - ab - (a^2)b - (b^2)a + (a^2)(b^2) = c - // a + b - ab - ab - ab + ab = c - // a + b - 2ab = c - // -2a * b = c - a - b - // 2a * b = a + b - c - // (a + a) * b = a + b - c - cs.enforce( - || "xor constraint", - |lc| lc + a.variable + a.variable, - |lc| lc + b.variable, - |lc| lc + a.variable + b.variable - result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } - - /// Performs an AND operation over the two operands, returning - /// an `AllocatedBit`. - pub fn and(mut cs: CS, a: &Self, b: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "and result", - || { - if *a.value.get()? & *b.value.get()? { - result_value = Some(true); - - Ok(Scalar::one()) - } else { - result_value = Some(false); - - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain (a) * (b) = (c), ensuring c is 1 iff - // a AND b are both 1. - cs.enforce( - || "and constraint", - |lc| lc + a.variable, - |lc| lc + b.variable, - |lc| lc + result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } - - /// Calculates `a AND (NOT b)`. - pub fn and_not(mut cs: CS, a: &Self, b: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "and not result", - || { - if *a.value.get()? & !*b.value.get()? { - result_value = Some(true); - - Ok(Scalar::one()) - } else { - result_value = Some(false); - - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain (a) * (1 - b) = (c), ensuring c is 1 iff - // a is true and b is false, and otherwise c is 0. - cs.enforce( - || "and not constraint", - |lc| lc + a.variable, - |lc| lc + CS::one() - b.variable, - |lc| lc + result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } - - /// Calculates `(NOT a) AND (NOT b)`. - pub fn nor(mut cs: CS, a: &Self, b: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let mut result_value = None; - - let result_var = cs.alloc( - || "nor result", - || { - if !*a.value.get()? & !*b.value.get()? { - result_value = Some(true); - - Ok(Scalar::one()) - } else { - result_value = Some(false); - - Ok(Scalar::zero()) - } - }, - )?; - - // Constrain (1 - a) * (1 - b) = (c), ensuring c is 1 iff - // a and b are both false, and otherwise c is 0. - cs.enforce( - || "nor constraint", - |lc| lc + CS::one() - a.variable, - |lc| lc + CS::one() - b.variable, - |lc| lc + result_var, - ); - - Ok(AllocatedBit { - variable: result_var, - value: result_value, - }) - } -} - -pub fn u64_into_boolean_vec_le>( - mut cs: CS, - value: Option, -) -> Result, SynthesisError> { - let values = match value { - Some(ref value) => { - let mut tmp = Vec::with_capacity(64); - - for i in 0..64 { - tmp.push(Some(*value >> i & 1 == 1)); - } - - tmp - } - None => vec![None; 64], - }; - - let bits = values - .into_iter() - .enumerate() - .map(|(i, b)| { - Ok(Boolean::from(AllocatedBit::alloc( - cs.namespace(|| format!("bit {}", i)), - b, - )?)) - }) - .collect::, SynthesisError>>()?; - - Ok(bits) -} - -pub fn field_into_boolean_vec_le< - Scalar: PrimeField, - CS: ConstraintSystem, - F: PrimeField, ->( - cs: CS, - value: Option, -) -> Result, SynthesisError> { - let v = field_into_allocated_bits_le::(cs, value)?; - - Ok(v.into_iter().map(Boolean::from).collect()) -} - -pub fn field_into_allocated_bits_le< - Scalar: PrimeField, - CS: ConstraintSystem, - F: PrimeField, ->( - mut cs: CS, - value: Option, -) -> Result, SynthesisError> { - // Deconstruct in big-endian bit order - let values = match value { - Some(ref value) => { - let mut field_char = BitIterator::::new(F::char()); - - let mut tmp = Vec::with_capacity(F::NUM_BITS as usize); - - let mut found_one = false; - for b in BitIterator::::new(value.to_repr()) { - // Skip leading bits - found_one |= field_char.next().unwrap(); - if !found_one { - continue; - } - - tmp.push(Some(b)); - } - - assert_eq!(tmp.len(), F::NUM_BITS as usize); - - tmp - } - None => vec![None; F::NUM_BITS as usize], - }; - - // Allocate in little-endian order - let bits = values - .into_iter() - .rev() - .enumerate() - .map(|(i, b)| AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), b)) - .collect::, SynthesisError>>()?; - - Ok(bits) -} - -/// This is a boolean value which may be either a constant or -/// an interpretation of an `AllocatedBit`. -#[derive(Clone)] -pub enum Boolean { - /// Existential view of the boolean variable - Is(AllocatedBit), - /// Negated view of the boolean variable - Not(AllocatedBit), - /// Constant (not an allocated variable) - Constant(bool), -} - -impl Boolean { - pub fn is_constant(&self) -> bool { - match *self { - Boolean::Constant(_) => true, - _ => false, - } - } - - pub fn enforce_equal(mut cs: CS, a: &Self, b: &Self) -> Result<(), SynthesisError> - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - match (a, b) { - (&Boolean::Constant(a), &Boolean::Constant(b)) => { - if a == b { - Ok(()) - } else { - Err(SynthesisError::Unsatisfiable) - } - } - (&Boolean::Constant(true), a) | (a, &Boolean::Constant(true)) => { - cs.enforce( - || "enforce equal to one", - |lc| lc, - |lc| lc, - |lc| lc + CS::one() - &a.lc(CS::one(), Scalar::one()), - ); - - Ok(()) - } - (&Boolean::Constant(false), a) | (a, &Boolean::Constant(false)) => { - cs.enforce( - || "enforce equal to zero", - |lc| lc, - |lc| lc, - |_| a.lc(CS::one(), Scalar::one()), - ); - - Ok(()) - } - (a, b) => { - cs.enforce( - || "enforce equal", - |lc| lc, - |lc| lc, - |_| a.lc(CS::one(), Scalar::one()) - &b.lc(CS::one(), Scalar::one()), - ); - - Ok(()) - } - } - } - - pub fn get_value(&self) -> Option { - match *self { - Boolean::Constant(c) => Some(c), - Boolean::Is(ref v) => v.get_value(), - Boolean::Not(ref v) => v.get_value().map(|b| !b), - } - } - - pub fn lc( - &self, - one: Variable, - coeff: Scalar, - ) -> LinearCombination { - match *self { - Boolean::Constant(c) => { - if c { - LinearCombination::::zero() + (coeff, one) - } else { - LinearCombination::::zero() - } - } - Boolean::Is(ref v) => LinearCombination::::zero() + (coeff, v.get_variable()), - Boolean::Not(ref v) => { - LinearCombination::::zero() + (coeff, one) - (coeff, v.get_variable()) - } - } - } - - /// Construct a boolean from a known constant - pub fn constant(b: bool) -> Self { - Boolean::Constant(b) - } - - /// Return a negated interpretation of this boolean. - pub fn not(&self) -> Self { - match *self { - Boolean::Constant(c) => Boolean::Constant(!c), - Boolean::Is(ref v) => Boolean::Not(v.clone()), - Boolean::Not(ref v) => Boolean::Is(v.clone()), - } - } - - /// Perform XOR over two boolean operands - pub fn xor<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - match (a, b) { - (&Boolean::Constant(false), x) | (x, &Boolean::Constant(false)) => Ok(x.clone()), - (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.not()), - // a XOR (NOT b) = NOT(a XOR b) - (is @ &Boolean::Is(_), not @ &Boolean::Not(_)) - | (not @ &Boolean::Not(_), is @ &Boolean::Is(_)) => { - Ok(Boolean::xor(cs, is, ¬.not())?.not()) - } - // a XOR b = (NOT a) XOR (NOT b) - (&Boolean::Is(ref a), &Boolean::Is(ref b)) - | (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { - Ok(Boolean::Is(AllocatedBit::xor(cs, a, b)?)) - } - } - } - - /// Perform AND over two boolean operands - pub fn and<'a, Scalar, CS>(cs: CS, a: &'a Self, b: &'a Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - match (a, b) { - // false AND x is always false - (&Boolean::Constant(false), _) | (_, &Boolean::Constant(false)) => { - Ok(Boolean::Constant(false)) - } - // true AND x is always x - (&Boolean::Constant(true), x) | (x, &Boolean::Constant(true)) => Ok(x.clone()), - // a AND (NOT b) - (&Boolean::Is(ref is), &Boolean::Not(ref not)) - | (&Boolean::Not(ref not), &Boolean::Is(ref is)) => { - Ok(Boolean::Is(AllocatedBit::and_not(cs, is, not)?)) - } - // (NOT a) AND (NOT b) = a NOR b - (&Boolean::Not(ref a), &Boolean::Not(ref b)) => { - Ok(Boolean::Is(AllocatedBit::nor(cs, a, b)?)) - } - // a AND b - (&Boolean::Is(ref a), &Boolean::Is(ref b)) => { - Ok(Boolean::Is(AllocatedBit::and(cs, a, b)?)) - } - } - } - - /// Computes (a and b) xor ((not a) and c) - pub fn sha256_ch<'a, Scalar, CS>( - mut cs: CS, - a: &'a Self, - b: &'a Self, - c: &'a Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let ch_value = match (a.get_value(), b.get_value(), c.get_value()) { - (Some(a), Some(b), Some(c)) => { - // (a and b) xor ((not a) and c) - Some((a & b) ^ ((!a) & c)) - } - _ => None, - }; - - match (a, b, c) { - (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { - // They're all constants, so we can just compute the value. - - return Ok(Boolean::Constant(ch_value.expect("they're all constants"))); - } - (&Boolean::Constant(false), _, c) => { - // If a is false - // (a and b) xor ((not a) and c) - // equals - // (false) xor (c) - // equals - // c - return Ok(c.clone()); - } - (a, &Boolean::Constant(false), c) => { - // If b is false - // (a and b) xor ((not a) and c) - // equals - // ((not a) and c) - return Boolean::and(cs, &a.not(), &c); - } - (a, b, &Boolean::Constant(false)) => { - // If c is false - // (a and b) xor ((not a) and c) - // equals - // (a and b) - return Boolean::and(cs, &a, &b); - } - (a, b, &Boolean::Constant(true)) => { - // If c is true - // (a and b) xor ((not a) and c) - // equals - // (a and b) xor (not a) - // equals - // not (a and (not b)) - return Ok(Boolean::and(cs, &a, &b.not())?.not()); - } - (a, &Boolean::Constant(true), c) => { - // If b is true - // (a and b) xor ((not a) and c) - // equals - // a xor ((not a) and c) - // equals - // not ((not a) and (not c)) - return Ok(Boolean::and(cs, &a.not(), &c.not())?.not()); - } - (&Boolean::Constant(true), _, _) => { - // If a is true - // (a and b) xor ((not a) and c) - // equals - // b xor ((not a) and c) - // So we just continue! - } - (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Not(_)) => {} - } - - let ch = cs.alloc( - || "ch", - || { - ch_value - .get() - .map(|v| if *v { Scalar::one() } else { Scalar::zero() }) - }, - )?; - - // a(b - c) = ch - c - cs.enforce( - || "ch computation", - |_| b.lc(CS::one(), Scalar::one()) - &c.lc(CS::one(), Scalar::one()), - |_| a.lc(CS::one(), Scalar::one()), - |lc| lc + ch - &c.lc(CS::one(), Scalar::one()), - ); - - Ok(AllocatedBit { - value: ch_value, - variable: ch, - } - .into()) - } - - /// Computes (a and b) xor (a and c) xor (b and c) - pub fn sha256_maj<'a, Scalar, CS>( - mut cs: CS, - a: &'a Self, - b: &'a Self, - c: &'a Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - let maj_value = match (a.get_value(), b.get_value(), c.get_value()) { - (Some(a), Some(b), Some(c)) => { - // (a and b) xor (a and c) xor (b and c) - Some((a & b) ^ (a & c) ^ (b & c)) - } - _ => None, - }; - - match (a, b, c) { - (&Boolean::Constant(_), &Boolean::Constant(_), &Boolean::Constant(_)) => { - // They're all constants, so we can just compute the value. - - return Ok(Boolean::Constant(maj_value.expect("they're all constants"))); - } - (&Boolean::Constant(false), b, c) => { - // If a is false, - // (a and b) xor (a and c) xor (b and c) - // equals - // (b and c) - return Boolean::and(cs, b, c); - } - (a, &Boolean::Constant(false), c) => { - // If b is false, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and c) - return Boolean::and(cs, a, c); - } - (a, b, &Boolean::Constant(false)) => { - // If c is false, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and b) - return Boolean::and(cs, a, b); - } - (a, b, &Boolean::Constant(true)) => { - // If c is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a and b) xor (a) xor (b) - // equals - // not ((not a) and (not b)) - return Ok(Boolean::and(cs, &a.not(), &b.not())?.not()); - } - (a, &Boolean::Constant(true), c) => { - // If b is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (a) xor (a and c) xor (c) - return Ok(Boolean::and(cs, &a.not(), &c.not())?.not()); - } - (&Boolean::Constant(true), b, c) => { - // If a is true, - // (a and b) xor (a and c) xor (b and c) - // equals - // (b) xor (c) xor (b and c) - return Ok(Boolean::and(cs, &b.not(), &c.not())?.not()); - } - (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Is(_), &Boolean::Not(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Is(_), &Boolean::Not(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Is(_)) - | (&Boolean::Not(_), &Boolean::Not(_), &Boolean::Not(_)) => {} - } - - let maj = cs.alloc( - || "maj", - || { - maj_value - .get() - .map(|v| if *v { Scalar::one() } else { Scalar::zero() }) - }, - )?; - - // ¬(¬a ∧ ¬b) ∧ ¬(¬a ∧ ¬c) ∧ ¬(¬b ∧ ¬c) - // (1 - ((1 - a) * (1 - b))) * (1 - ((1 - a) * (1 - c))) * (1 - ((1 - b) * (1 - c))) - // (a + b - ab) * (a + c - ac) * (b + c - bc) - // -2abc + ab + ac + bc - // a (-2bc + b + c) + bc - // - // (b) * (c) = (bc) - // (2bc - b - c) * (a) = bc - maj - - let bc = Self::and(cs.namespace(|| "b and c"), b, c)?; - - cs.enforce( - || "maj computation", - |_| { - bc.lc(CS::one(), Scalar::one()) + &bc.lc(CS::one(), Scalar::one()) - - &b.lc(CS::one(), Scalar::one()) - - &c.lc(CS::one(), Scalar::one()) - }, - |_| a.lc(CS::one(), Scalar::one()), - |_| bc.lc(CS::one(), Scalar::one()) - maj, - ); - - Ok(AllocatedBit { - value: maj_value, - variable: maj, - } - .into()) - } -} - -impl From for Boolean { - fn from(b: AllocatedBit) -> Boolean { - Boolean::Is(b) - } -} - -#[cfg(test)] -mod test { - use super::{field_into_allocated_bits_le, u64_into_boolean_vec_le, AllocatedBit, Boolean}; - use crate::gadgets::test::*; - use crate::ConstraintSystem; - use bls12_381::Scalar; - use ff::{Field, PrimeField}; - - #[test] - fn test_allocated_bit() { - let mut cs = TestConstraintSystem::new(); - - AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - assert!(cs.get("boolean") == Scalar::one()); - assert!(cs.is_satisfied()); - cs.set("boolean", Scalar::zero()); - assert!(cs.is_satisfied()); - cs.set("boolean", Scalar::from_str("2").unwrap()); - assert!(!cs.is_satisfied()); - assert!(cs.which_is_unsatisfied() == Some("boolean constraint")); - } - - #[test] - fn test_xor() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::xor(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), *a_val ^ *b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("xor result") - == if *a_val ^ *b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "xor result", - if *a_val ^ *b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_and() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::and(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), *a_val & *b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("and result") - == if *a_val & *b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "and result", - if *a_val & *b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_and_not() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::and_not(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), *a_val & !*b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("and not result") - == if *a_val & !*b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "and not result", - if *a_val & !*b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_nor() { - for a_val in [false, true].iter() { - for b_val in [false, true].iter() { - let mut cs = TestConstraintSystem::::new(); - let a = AllocatedBit::alloc(cs.namespace(|| "a"), Some(*a_val)).unwrap(); - let b = AllocatedBit::alloc(cs.namespace(|| "b"), Some(*b_val)).unwrap(); - let c = AllocatedBit::nor(&mut cs, &a, &b).unwrap(); - assert_eq!(c.value.unwrap(), !*a_val & !*b_val); - - assert!(cs.is_satisfied()); - assert!(cs.get("a/boolean") == if *a_val { Field::one() } else { Field::zero() }); - assert!(cs.get("b/boolean") == if *b_val { Field::one() } else { Field::zero() }); - assert!( - cs.get("nor result") - == if !*a_val & !*b_val { - Field::one() - } else { - Field::zero() - } - ); - - // Invert the result and check if the constraint system is still satisfied - cs.set( - "nor result", - if !*a_val & !*b_val { - Field::zero() - } else { - Field::one() - }, - ); - assert!(!cs.is_satisfied()); - } - } - } - - #[test] - fn test_enforce_equal() { - for a_bool in [false, true].iter().cloned() { - for b_bool in [false, true].iter().cloned() { - for a_neg in [false, true].iter().cloned() { - for b_neg in [false, true].iter().cloned() { - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), - ); - let mut b = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_bool)).unwrap(), - ); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); - - assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); - } - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::Constant(a_bool); - let mut b = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "b"), Some(b_bool)).unwrap(), - ); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); - - assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); - } - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::from( - AllocatedBit::alloc(cs.namespace(|| "a"), Some(a_bool)).unwrap(), - ); - let mut b = Boolean::Constant(b_bool); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - Boolean::enforce_equal(&mut cs, &a, &b).unwrap(); - - assert_eq!(cs.is_satisfied(), (a_bool ^ a_neg) == (b_bool ^ b_neg)); - } - { - let mut cs = TestConstraintSystem::::new(); - - let mut a = Boolean::Constant(a_bool); - let mut b = Boolean::Constant(b_bool); - - if a_neg { - a = a.not(); - } - if b_neg { - b = b.not(); - } - - let result = Boolean::enforce_equal(&mut cs, &a, &b); - - if (a_bool ^ a_neg) == (b_bool ^ b_neg) { - assert!(result.is_ok()); - assert!(cs.is_satisfied()); - } else { - assert!(result.is_err()); - } - } - } - } - } - } - } - - #[test] - fn test_boolean_negation() { - let mut cs = TestConstraintSystem::::new(); - - let mut b = Boolean::from(AllocatedBit::alloc(&mut cs, Some(true)).unwrap()); - - match b { - Boolean::Is(_) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Not(_) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Is(_) => {} - _ => panic!("unexpected value"), - } - - b = Boolean::constant(true); - - match b { - Boolean::Constant(true) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Constant(false) => {} - _ => panic!("unexpected value"), - } - - b = b.not(); - - match b { - Boolean::Constant(true) => {} - _ => panic!("unexpected value"), - } - } - - #[derive(Copy, Clone, Debug)] - enum OperandType { - True, - False, - AllocatedTrue, - AllocatedFalse, - NegatedAllocatedTrue, - NegatedAllocatedFalse, - } - - impl OperandType { - fn is_constant(&self) -> bool { - match *self { - OperandType::True => true, - OperandType::False => true, - OperandType::AllocatedTrue => false, - OperandType::AllocatedFalse => false, - OperandType::NegatedAllocatedTrue => false, - OperandType::NegatedAllocatedFalse => false, - } - } - - fn val(&self) -> bool { - match *self { - OperandType::True => true, - OperandType::False => false, - OperandType::AllocatedTrue => true, - OperandType::AllocatedFalse => false, - OperandType::NegatedAllocatedTrue => false, - OperandType::NegatedAllocatedFalse => true, - } - } - } - - #[test] - fn test_boolean_xor() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); - - let a; - let b; - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - } - - let c = Boolean::xor(&mut cs, &a, &b).unwrap(); - - assert!(cs.is_satisfied()); - - match (first_operand, second_operand, c) { - (OperandType::True, OperandType::True, Boolean::Constant(false)) => {} - (OperandType::True, OperandType::False, Boolean::Constant(true)) => {} - (OperandType::True, OperandType::AllocatedTrue, Boolean::Not(_)) => {} - (OperandType::True, OperandType::AllocatedFalse, Boolean::Not(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Is(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Is(_)) => {} - - (OperandType::False, OperandType::True, Boolean::Constant(true)) => {} - (OperandType::False, OperandType::False, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::AllocatedTrue, Boolean::Is(_)) => {} - (OperandType::False, OperandType::AllocatedFalse, Boolean::Is(_)) => {} - (OperandType::False, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {} - (OperandType::False, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {} - - (OperandType::AllocatedTrue, OperandType::True, Boolean::Not(_)) => {} - (OperandType::AllocatedTrue, OperandType::False, Boolean::Is(_)) => {} - ( - OperandType::AllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - (OperandType::AllocatedFalse, OperandType::True, Boolean::Not(_)) => {} - (OperandType::AllocatedFalse, OperandType::False, Boolean::Is(_)) => {} - ( - OperandType::AllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Is(_)) => {} - (OperandType::NegatedAllocatedTrue, OperandType::False, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Is(_)) => {} - (OperandType::NegatedAllocatedFalse, OperandType::False, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Not(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("xor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - _ => panic!("this should never be encountered"), - } - } - } - } - - #[test] - fn test_boolean_and() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::::new(); - - let a; - let b; - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()).not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()).not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - } - - let c = Boolean::and(&mut cs, &a, &b).unwrap(); - - assert!(cs.is_satisfied()); - - match (first_operand, second_operand, c) { - (OperandType::True, OperandType::True, Boolean::Constant(true)) => {} - (OperandType::True, OperandType::False, Boolean::Constant(false)) => {} - (OperandType::True, OperandType::AllocatedTrue, Boolean::Is(_)) => {} - (OperandType::True, OperandType::AllocatedFalse, Boolean::Is(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedTrue, Boolean::Not(_)) => {} - (OperandType::True, OperandType::NegatedAllocatedFalse, Boolean::Not(_)) => {} - - (OperandType::False, OperandType::True, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::False, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::AllocatedTrue, Boolean::Constant(false)) => {} - (OperandType::False, OperandType::AllocatedFalse, Boolean::Constant(false)) => { - } - ( - OperandType::False, - OperandType::NegatedAllocatedTrue, - Boolean::Constant(false), - ) => {} - ( - OperandType::False, - OperandType::NegatedAllocatedFalse, - Boolean::Constant(false), - ) => {} - - (OperandType::AllocatedTrue, OperandType::True, Boolean::Is(_)) => {} - (OperandType::AllocatedTrue, OperandType::False, Boolean::Constant(false)) => {} - ( - OperandType::AllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - (OperandType::AllocatedFalse, OperandType::True, Boolean::Is(_)) => {} - (OperandType::AllocatedFalse, OperandType::False, Boolean::Constant(false)) => { - } - ( - OperandType::AllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - (OperandType::NegatedAllocatedTrue, OperandType::True, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedTrue, - OperandType::False, - Boolean::Constant(false), - ) => {} - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - - (OperandType::NegatedAllocatedFalse, OperandType::True, Boolean::Not(_)) => {} - ( - OperandType::NegatedAllocatedFalse, - OperandType::False, - Boolean::Constant(false), - ) => {} - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::AllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("and not result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedTrue, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::zero()); - assert_eq!(v.value, Some(false)); - } - ( - OperandType::NegatedAllocatedFalse, - OperandType::NegatedAllocatedFalse, - Boolean::Is(ref v), - ) => { - assert!(cs.get("nor result") == Field::one()); - assert_eq!(v.value, Some(true)); - } - - _ => { - panic!( - "unexpected behavior at {:?} AND {:?}", - first_operand, second_operand - ); - } - } - } - } - } - - #[test] - fn test_u64_into_boolean_vec_le() { - let mut cs = TestConstraintSystem::::new(); - - let bits = u64_into_boolean_vec_le(&mut cs, Some(17234652694787248421)).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(bits.len(), 64); - - assert_eq!(bits[63 - 0].get_value().unwrap(), true); - assert_eq!(bits[63 - 1].get_value().unwrap(), true); - assert_eq!(bits[63 - 2].get_value().unwrap(), true); - assert_eq!(bits[63 - 3].get_value().unwrap(), false); - assert_eq!(bits[63 - 4].get_value().unwrap(), true); - assert_eq!(bits[63 - 5].get_value().unwrap(), true); - assert_eq!(bits[63 - 20].get_value().unwrap(), true); - assert_eq!(bits[63 - 21].get_value().unwrap(), false); - assert_eq!(bits[63 - 22].get_value().unwrap(), false); - } - - #[test] - fn test_field_into_allocated_bits_le() { - let mut cs = TestConstraintSystem::::new(); - - let r = Scalar::from_str( - "9147677615426976802526883532204139322118074541891858454835346926874644257775", - ) - .unwrap(); - - let bits = field_into_allocated_bits_le(&mut cs, Some(r)).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(bits.len(), 255); - - assert_eq!(bits[254 - 0].value.unwrap(), false); - assert_eq!(bits[254 - 1].value.unwrap(), false); - assert_eq!(bits[254 - 2].value.unwrap(), true); - assert_eq!(bits[254 - 3].value.unwrap(), false); - assert_eq!(bits[254 - 4].value.unwrap(), true); - assert_eq!(bits[254 - 5].value.unwrap(), false); - assert_eq!(bits[254 - 20].value.unwrap(), true); - assert_eq!(bits[254 - 23].value.unwrap(), true); - } - - #[test] - fn test_boolean_sha256_ch() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - for third_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::new(); - - let a; - let b; - let c; - - // ch = (a and b) xor ((not a) and c) - let expected = (first_operand.val() & second_operand.val()) - ^ ((!first_operand.val()) & third_operand.val()); - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - .not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - .not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - c = dyn_construct(third_operand, "c"); - } - - let maj = Boolean::sha256_ch(&mut cs, &a, &b, &c).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(maj.get_value().unwrap(), expected); - - if first_operand.is_constant() - || second_operand.is_constant() - || third_operand.is_constant() - { - if first_operand.is_constant() - && second_operand.is_constant() - && third_operand.is_constant() - { - assert_eq!(cs.num_constraints(), 0); - } - } else { - assert_eq!(cs.get("ch"), { - if expected { - Scalar::one() - } else { - Scalar::zero() - } - }); - cs.set("ch", { - if expected { - Scalar::zero() - } else { - Scalar::one() - } - }); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "ch computation"); - } - } - } - } - } - - #[test] - fn test_boolean_sha256_maj() { - let variants = [ - OperandType::True, - OperandType::False, - OperandType::AllocatedTrue, - OperandType::AllocatedFalse, - OperandType::NegatedAllocatedTrue, - OperandType::NegatedAllocatedFalse, - ]; - - for first_operand in variants.iter().cloned() { - for second_operand in variants.iter().cloned() { - for third_operand in variants.iter().cloned() { - let mut cs = TestConstraintSystem::new(); - - let a; - let b; - let c; - - // maj = (a and b) xor (a and c) xor (b and c) - let expected = (first_operand.val() & second_operand.val()) - ^ (first_operand.val() & third_operand.val()) - ^ (second_operand.val() & third_operand.val()); - - { - let mut dyn_construct = |operand, name| { - let cs = cs.namespace(|| name); - - match operand { - OperandType::True => Boolean::constant(true), - OperandType::False => Boolean::constant(false), - OperandType::AllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - } - OperandType::AllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - } - OperandType::NegatedAllocatedTrue => { - Boolean::from(AllocatedBit::alloc(cs, Some(true)).unwrap()) - .not() - } - OperandType::NegatedAllocatedFalse => { - Boolean::from(AllocatedBit::alloc(cs, Some(false)).unwrap()) - .not() - } - } - }; - - a = dyn_construct(first_operand, "a"); - b = dyn_construct(second_operand, "b"); - c = dyn_construct(third_operand, "c"); - } - - let maj = Boolean::sha256_maj(&mut cs, &a, &b, &c).unwrap(); - - assert!(cs.is_satisfied()); - - assert_eq!(maj.get_value().unwrap(), expected); - - if first_operand.is_constant() - || second_operand.is_constant() - || third_operand.is_constant() - { - if first_operand.is_constant() - && second_operand.is_constant() - && third_operand.is_constant() - { - assert_eq!(cs.num_constraints(), 0); - } - } else { - assert_eq!(cs.get("maj"), { - if expected { - Scalar::one() - } else { - Scalar::zero() - } - }); - cs.set("maj", { - if expected { - Scalar::zero() - } else { - Scalar::one() - } - }); - assert_eq!(cs.which_is_unsatisfied().unwrap(), "maj computation"); - } - } - } - } - } - - #[test] - fn test_alloc_conditionally() { - { - let mut cs = TestConstraintSystem::::new(); - let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); - - let value = None; - // if value is none, fail with SynthesisError - let is_err = AllocatedBit::alloc_conditionally( - cs.namespace(|| "alloc_conditionally"), - value, - &b, - ) - .is_err(); - assert!(is_err); - } - - { - // since value is true, b must be false, so it should succeed - let mut cs = TestConstraintSystem::::new(); - - let value = Some(true); - let b = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); - let allocated_value = AllocatedBit::alloc_conditionally( - cs.namespace(|| "alloc_conditionally"), - value, - &b, - ) - .unwrap(); - - assert_eq!(allocated_value.get_value().unwrap(), true); - assert!(cs.is_satisfied()); - } - - { - // since value is true, b must be false, so it should fail - let mut cs = TestConstraintSystem::::new(); - - let value = Some(true); - let b = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b) - .unwrap(); - - assert!(!cs.is_satisfied()); - } - - { - // since value is false, we don't care about the value of the bit - - let value = Some(false); - //check with false bit - let mut cs = TestConstraintSystem::::new(); - let b1 = AllocatedBit::alloc(&mut cs, Some(false)).unwrap(); - AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b1) - .unwrap(); - - assert!(cs.is_satisfied()); - - //check with true bit - let mut cs = TestConstraintSystem::::new(); - let b2 = AllocatedBit::alloc(&mut cs, Some(true)).unwrap(); - AllocatedBit::alloc_conditionally(cs.namespace(|| "alloc_conditionally"), value, &b2) - .unwrap(); - - assert!(cs.is_satisfied()); - } - } -} diff --git a/bellman/src/gadgets/lookup.rs b/bellman/src/gadgets/lookup.rs deleted file mode 100644 index 2ef6f697c..000000000 --- a/bellman/src/gadgets/lookup.rs +++ /dev/null @@ -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, -{ - 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( - mut cs: CS, - bits: &[Boolean], - coords: &[(Scalar, Scalar)], -) -> Result<(AllocatedNum, AllocatedNum), SynthesisError> -where - CS: ConstraintSystem, -{ - 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::(3, coords.iter().map(|c| &c.0), &mut x_coeffs); - synth::(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::(one, x_coeffs[0b011]) - + &bits[2].lc::(one, x_coeffs[0b101]) - + &precomp.lc::(one, x_coeffs[0b111]) - }, - |lc| lc + &bits[0].lc::(one, Scalar::one()), - |lc| { - lc + res_x.get_variable() - - (x_coeffs[0b000], one) - - &bits[1].lc::(one, x_coeffs[0b010]) - - &bits[2].lc::(one, x_coeffs[0b100]) - - &precomp.lc::(one, x_coeffs[0b110]) - }, - ); - - cs.enforce( - || "y-coordinate lookup", - |lc| { - lc + (y_coeffs[0b001], one) - + &bits[1].lc::(one, y_coeffs[0b011]) - + &bits[2].lc::(one, y_coeffs[0b101]) - + &precomp.lc::(one, y_coeffs[0b111]) - }, - |lc| lc + &bits[0].lc::(one, Scalar::one()), - |lc| { - lc + res_y.get_variable() - - (y_coeffs[0b000], one) - - &bits[1].lc::(one, y_coeffs[0b010]) - - &bits[2].lc::(one, y_coeffs[0b100]) - - &precomp.lc::(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( - mut cs: CS, - bits: &[Boolean], - coords: &[(Scalar, Scalar)], -) -> Result<(Num, Num), SynthesisError> -where - CS: ConstraintSystem, -{ - 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::(2, coords.iter().map(|c| &c.0), &mut x_coeffs); - synth::(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::(one, y_coeffs[0b11]) - + &bits[1].lc::(one, y_coeffs[0b10]) - + &bits[0].lc::(one, y_coeffs[0b01]) - + (y_coeffs[0b00], one); - - cs.enforce( - || "y-coordinate lookup", - |lc| lc + &y_lc + &y_lc, - |lc| lc + &bits[2].lc::(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]); - } - } -} diff --git a/bellman/src/gadgets/multieq.rs b/bellman/src/gadgets/multieq.rs deleted file mode 100644 index b2bc151cb..000000000 --- a/bellman/src/gadgets/multieq.rs +++ /dev/null @@ -1,123 +0,0 @@ -use ff::PrimeField; - -use crate::{ConstraintSystem, LinearCombination, SynthesisError, Variable}; - -pub struct MultiEq> { - cs: CS, - ops: usize, - bits_used: usize, - lhs: LinearCombination, - rhs: LinearCombination, -} - -impl> MultiEq { - 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, - rhs: &LinearCombination, - ) { - // 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> Drop for MultiEq { - fn drop(&mut self) { - if self.bits_used > 0 { - self.accumulate(); - } - } -} - -impl> ConstraintSystem - for MultiEq -{ - type Root = Self; - - fn one() -> Variable { - CS::one() - } - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.cs.alloc(annotation, f) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.cs.alloc_input(annotation, f) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - self.cs.enforce(annotation, a, b, c) - } - - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - 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 - } -} diff --git a/bellman/src/gadgets/multipack.rs b/bellman/src/gadgets/multipack.rs deleted file mode 100644 index fda716e67..000000000 --- a/bellman/src/gadgets/multipack.rs +++ /dev/null @@ -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(mut cs: CS, bits: &[Boolean]) -> Result<(), SynthesisError> -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - for (i, bits) in bits.chunks(Scalar::CAPACITY as usize).enumerate() { - let mut num = Num::::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 { - 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 { - bytes - .iter() - .flat_map(|&v| (0..8).map(move |i| (v >> i) & 1 == 1)) - .collect() -} - -pub fn compute_multipacking(bits: &[bool]) -> Vec { - 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::::new(); - - let bits: Vec = (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::>(); - - let expected_inputs = compute_multipacking(&bits); - - pack_into_inputs(cs.namespace(|| "pack"), &circuit_bits).unwrap(); - - assert!(cs.is_satisfied()); - assert!(cs.verify(&expected_inputs)); - } -} diff --git a/bellman/src/gadgets/num.rs b/bellman/src/gadgets/num.rs deleted file mode 100644 index f0267579d..000000000 --- a/bellman/src/gadgets/num.rs +++ /dev/null @@ -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 { - value: Option, - variable: Variable, -} - -impl Clone for AllocatedNum { - fn clone(&self) -> Self { - AllocatedNum { - value: self.value, - variable: self.variable, - } - } -} - -impl AllocatedNum { - pub fn alloc(mut cs: CS, value: F) -> Result - where - CS: ConstraintSystem, - F: FnOnce() -> Result, - { - 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(&self, mut cs: CS) -> Result<(), SynthesisError> - where - CS: ConstraintSystem, - { - 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(&self, mut cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - pub fn kary_and( - mut cs: CS, - v: &[AllocatedBit], - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - 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::::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::::new(b) { - let a_bit = a.as_mut().map(|e| e.next().unwrap()); - - // Skip over unset bits at the beginning - found_one |= b; - if !found_one { - // a_bit should also be false - a_bit.map(|e| assert!(!e)); - continue; - } - - if b { - // This is part of a run of ones. Let's just - // allocate the boolean with the expected value. - let a_bit = AllocatedBit::alloc(cs.namespace(|| format!("bit {}", i)), a_bit)?; - // ... and add it to the current run of ones. - current_run.push(a_bit.clone()); - result.push(a_bit); - } else { - if !current_run.is_empty() { - // This is the start of a run of zeros, but we need - // to k-ary AND against `last_run` first. - - if last_run.is_some() { - current_run.push(last_run.clone().unwrap()); - } - last_run = Some(kary_and( - cs.namespace(|| format!("run ending at {}", i)), - ¤t_run, - )?); - current_run.truncate(0); - } - - // If `last_run` is true, `a` must be false, or it would - // not be in the field. - // - // If `last_run` is false, `a` can be true or false. - - let a_bit = AllocatedBit::alloc_conditionally( - cs.namespace(|| format!("bit {}", i)), - a_bit, - &last_run.as_ref().expect("char always starts with a one"), - )?; - result.push(a_bit); - } - - i += 1; - } - - // char is prime, so we'll always end on - // a run of zeros. - assert_eq!(current_run.len(), 0); - - // Now, we have `result` in big-endian order. - // However, now we have to unpack self! - - let mut lc = LinearCombination::zero(); - let mut coeff = Scalar::one(); - - for bit in result.iter().rev() { - lc = lc + (coeff, bit.get_variable()); - - coeff = coeff.double(); - } - - lc = lc - self.variable; - - cs.enforce(|| "unpacking constraint", |lc| lc, |lc| lc, |_| lc); - - // Convert into booleans, and reverse for little-endian bit order - Ok(result.into_iter().map(Boolean::from).rev().collect()) - } - - /// Convert the allocated number into its little-endian representation. - /// Note that this does not strongly enforce that the commitment is - /// "in the field." - pub fn to_bits_le(&self, mut cs: CS) -> Result, SynthesisError> - where - CS: ConstraintSystem, - { - 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(&self, mut cs: CS, other: &Self) -> Result - where - CS: ConstraintSystem, - { - 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(&self, mut cs: CS) -> Result - where - CS: ConstraintSystem, - { - 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(&self, mut cs: CS) -> Result<(), SynthesisError> - where - CS: ConstraintSystem, - { - 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( - mut cs: CS, - a: &Self, - b: &Self, - condition: &Boolean, - ) -> Result<(Self, Self), SynthesisError> - where - CS: ConstraintSystem, - { - 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 { - self.value - } - - pub fn get_variable(&self) -> Variable { - self.variable - } -} - -pub struct Num { - value: Option, - lc: LinearCombination, -} - -impl From> for Num { - fn from(num: AllocatedNum) -> Num { - Num { - value: num.value, - lc: LinearCombination::::zero() + num.variable, - } - } -} - -impl Num { - pub fn zero() -> Self { - Num { - value: Some(Scalar::zero()), - lc: LinearCombination::zero(), - } - } - - pub fn get_value(&self) -> Option { - self.value - } - - pub fn lc(&self, coeff: Scalar) -> LinearCombination { - 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::::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()); - } - } - } -} diff --git a/bellman/src/gadgets/sha256.rs b/bellman/src/gadgets/sha256.rs deleted file mode 100644 index 4ffc03254..000000000 --- a/bellman/src/gadgets/sha256.rs +++ /dev/null @@ -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( - mut cs: CS, - input: &[Boolean], -) -> Result, SynthesisError> -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - 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(mut cs: CS, input: &[Boolean]) -> Result, SynthesisError> -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - 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 { - IV.iter().map(|&v| UInt32::constant(v)).collect() -} - -fn sha256_compression_function( - cs: CS, - input: &[Boolean], - current_hash_value: &[UInt32], -) -> Result, SynthesisError> -where - Scalar: PrimeField, - CS: ConstraintSystem, -{ - 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::>(); - - // 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), - Concrete(UInt32), - } - - impl Maybe { - fn compute(self, cs: M, others: &[UInt32]) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - M: ConstraintSystem>, - { - 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::>()); - d = c; - c = b; - b = new_a; - a = Maybe::Deferred( - temp1 - .iter() - .cloned() - .chain(temp2.iter().cloned()) - .collect::>(), - ); - } - - /* - 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::::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::::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 = (0..input_len).map(|_| rng.next_u32() as u8).collect(); - h.update(&data); - let hash_result = h.finalize(); - - let mut cs = TestConstraintSystem::::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); - } - } - } - } - } -} diff --git a/bellman/src/gadgets/test/mod.rs b/bellman/src/gadgets/test/mod.rs deleted file mode 100644 index 08f5391ed..000000000 --- a/bellman/src/gadgets/test/mod.rs +++ /dev/null @@ -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 { - named_objects: HashMap, - current_namespace: Vec, - constraints: Vec<( - LinearCombination, - LinearCombination, - LinearCombination, - 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 { - 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(terms: &[(Variable, Scalar)]) -> BTreeMap { - 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(terms: &[(Variable, Scalar)], h: &mut Blake2sState) { - let map = proc_lc::(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(); - ::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( - 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 TestConstraintSystem { - pub fn new() -> TestConstraintSystem { - let mut map = HashMap::new(); - map.insert( - "ONE".into(), - NamedObject::Var(TestConstraintSystem::::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::>(); - - let pp = |s: &mut String, lc: &LinearCombination| { - write!(s, "(").unwrap(); - let mut is_first = true; - for (var, coeff) in proc_lc::(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::(constraint.0.as_ref(), &mut h); - hash_lc::(constraint.1.as_ref(), &mut h); - hash_lc::(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::(a.as_ref(), &self.inputs, &self.aux); - let b = eval_lc::(b.as_ref(), &self.inputs, &self.aux); - let c = eval_lc::(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 ConstraintSystem for TestConstraintSystem { - type Root = Self; - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - 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(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - 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(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - 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(&mut self, name_fn: N) - where - NR: Into, - 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::::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()); -} diff --git a/bellman/src/gadgets/uint32.rs b/bellman/src/gadgets/uint32.rs deleted file mode 100644 index db5e2d506..000000000 --- a/bellman/src/gadgets/uint32.rs +++ /dev/null @@ -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, - value: Option, -} - -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(mut cs: CS, value: Option) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - 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::, SynthesisError>>()?; - - Ok(UInt32 { bits, value }) - } - - pub fn into_bits_be(self) -> Vec { - 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 { - 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( - mut cs: CS, - a: &Self, - b: &Self, - c: &Self, - tri_fn: F, - circuit_fn: U, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - F: Fn(u32, u32, u32) -> u32, - U: Fn(&mut CS, usize, &Boolean, &Boolean, &Boolean) -> Result, - { - 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::>()?; - - 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( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - 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( - cs: CS, - a: &Self, - b: &Self, - c: &Self, - ) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - 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(&self, mut cs: CS, other: &Self) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - { - 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::>()?; - - Ok(UInt32 { - bits, - value: new_value, - }) - } - - /// Perform modular addition of several `UInt32` objects. - pub fn addmany(mut cs: M, operands: &[Self]) -> Result - where - Scalar: PrimeField, - CS: ConstraintSystem, - M: ConstraintSystem>, - { - // 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::>(); - - 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::>(); - - 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::::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::::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::::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::::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::::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; - } - } - } -} diff --git a/bellman/src/groth16/generator.rs b/bellman/src/groth16/generator.rs deleted file mode 100644 index b78efcec3..000000000 --- a/bellman/src/groth16/generator.rs +++ /dev/null @@ -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( - circuit: C, - rng: &mut R, -) -> Result, SynthesisError> -where - E: Engine, - E::G1: WnafGroup, - E::G2: WnafGroup, - C: Circuit, - 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::(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 { - num_inputs: usize, - num_aux: usize, - num_constraints: usize, - at_inputs: Vec>, - bt_inputs: Vec>, - ct_inputs: Vec>, - at_aux: Vec>, - bt_aux: Vec>, - ct_aux: Vec>, -} - -impl ConstraintSystem for KeypairAssembly { - type Root = Self; - - fn alloc(&mut self, _: A, _: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - // 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(&mut self, _: A, _: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - // 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(&mut self, _: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - fn eval( - l: LinearCombination, - 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(&mut self, _: N) - where - NR: Into, - 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( - circuit: C, - g1: E::G1, - g2: E::G2, - alpha: E::Fr, - beta: E::Fr, - gamma: E::Fr, - delta: E::Fr, - tau: E::Fr, -) -> Result, SynthesisError> -where - E: Engine, - E::G1: WnafGroup, - E::G2: WnafGroup, - C: Circuit, -{ - 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::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( - // wNAF window tables - g1_wnaf: &Wnaf>, - g2_wnaf: &Wnaf>, - - // Lagrange coefficients for tau - powers_of_tau: &[Scalar], - - // 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( - powers_of_tau: &[Scalar], - 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::( - &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::( - &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:: { - 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(), - ), - }) -} diff --git a/bellman/src/groth16/mod.rs b/bellman/src/groth16/mod.rs deleted file mode 100644 index 462dee38b..000000000 --- a/bellman/src/groth16/mod.rs +++ /dev/null @@ -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 { - pub a: E::G1Affine, - pub b: E::G2Affine, - pub c: E::G1Affine, -} - -impl PartialEq for Proof { - fn eq(&self, other: &Self) -> bool { - self.a == other.a && self.b == other.b && self.c == other.c - } -} - -impl Proof { - pub fn 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(mut reader: R) -> io::Result { - let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::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 { - let mut g2_repr = ::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 { - // 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, -} - -impl PartialEq for VerifyingKey { - 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 VerifyingKey { - pub fn 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::(self.ic.len() as u32)?; - for ic in &self.ic { - writer.write_all(ic.to_uncompressed().as_ref())?; - } - - Ok(()) - } - - pub fn read(mut reader: R) -> io::Result { - let read_g1 = |reader: &mut R| -> io::Result { - let mut g1_repr = ::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 { - let mut g2_repr = ::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::()? 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 { - pub vk: VerifyingKey, - - // 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>, - - // 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>, - - // 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>, - - // 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>, - pub b_g2: Arc>, -} - -impl PartialEq for Parameters { - 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 Parameters { - pub fn write(&self, mut writer: W) -> io::Result<()> { - self.vk.write(&mut writer)?; - - writer.write_u32::(self.h.len() as u32)?; - for g in &self.h[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - writer.write_u32::(self.l.len() as u32)?; - for g in &self.l[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - writer.write_u32::(self.a.len() as u32)?; - for g in &self.a[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - writer.write_u32::(self.b_g1.len() as u32)?; - for g in &self.b_g1[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - writer.write_u32::(self.b_g2.len() as u32)?; - for g in &self.b_g2[..] { - writer.write_all(g.to_uncompressed().as_ref())?; - } - - Ok(()) - } - - pub fn read(mut reader: R, checked: bool) -> io::Result { - let read_g1 = |reader: &mut R| -> io::Result { - let mut repr = ::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 { - let mut repr = ::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::::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::()? as usize; - for _ in 0..len { - h.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - l.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - a.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? as usize; - for _ in 0..len { - b_g1.push(read_g1(&mut reader)?); - } - } - - { - let len = reader.read_u32::()? 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 { - /// 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, -} - -pub trait ParameterSource { - type G1Builder: SourceBuilder; - type G2Builder: SourceBuilder; - - fn get_vk(&mut self, num_ic: usize) -> Result, SynthesisError>; - fn get_h(&mut self, num_h: usize) -> Result; - fn get_l(&mut self, num_l: usize) -> Result; - 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 for &'a Parameters { - type G1Builder = (Arc>, usize); - type G2Builder = (Arc>, usize); - - fn get_vk(&mut self, _: usize) -> Result, SynthesisError> { - Ok(self.vk.clone()) - } - - fn get_h(&mut self, _: usize) -> Result { - Ok((self.h.clone(), 0)) - } - - fn get_l(&mut self, _: usize) -> Result { - 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 { - a: Option, - b: Option, - } - - impl Circuit for MySillyCircuit { - fn synthesize>( - 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::(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::(¶ms.vk); - - for _ in 0..100 { - let a = Scalar::random(rng); - let b = Scalar::random(rng); - let mut c = a; - c.mul_assign(&b); - - let proof = create_random_proof( - MySillyCircuit { - a: Some(a), - b: Some(b), - }, - ¶ms, - rng, - ) - .unwrap(); - - let mut v = vec![]; - proof.write(&mut v).unwrap(); - - assert_eq!(v.len(), 192); - - let de_proof = Proof::read(&v[..]).unwrap(); - assert!(proof == de_proof); - - assert!(verify_proof(&pvk, &proof, &[c]).is_ok()); - assert!(verify_proof(&pvk, &proof, &[a]).is_err()); - } - } -} diff --git a/bellman/src/groth16/prover.rs b/bellman/src/groth16/prover.rs deleted file mode 100644 index 1f2d964a1..000000000 --- a/bellman/src/groth16/prover.rs +++ /dev/null @@ -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( - lc: &LinearCombination, - 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 { - // Density of queries - a_aux_density: DensityTracker, - b_input_density: DensityTracker, - b_aux_density: DensityTracker, - - // Evaluations of A, B, C polynomials - a: Vec>, - b: Vec>, - c: Vec>, - - // Assignments of variables - input_assignment: Vec, - aux_assignment: Vec, -} - -impl ConstraintSystem for ProvingAssignment { - type Root = Self; - - fn alloc(&mut self, _: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - 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(&mut self, _: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.input_assignment.push(f()?); - self.b_input_density.add_element(); - - Ok(Variable(Index::Input(self.input_assignment.len() - 1))) - } - - fn enforce(&mut self, _: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - 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(&mut self, _: N) - where - NR: Into, - 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>( - circuit: C, - params: P, - rng: &mut R, -) -> Result, SynthesisError> -where - E: Engine, - C: Circuit, - R: RngCore, -{ - let r = E::Fr::random(rng); - let s = E::Fr::random(rng); - - create_proof::(circuit, params, r, s) -} - -pub fn create_proof>( - circuit: C, - mut params: P, - r: E::Fr, - s: E::Fr, -) -> Result, SynthesisError> -where - E: Engine, - C: Circuit, -{ - 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::>()); - - 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::::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::::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(), - }) -} diff --git a/bellman/src/groth16/tests/dummy_engine.rs b/bellman/src/groth16/tests/dummy_engine.rs deleted file mode 100644 index fd7d2b915..000000000 --- a/bellman/src/groth16/tests/dummy_engine.rs +++ /dev/null @@ -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 = Wrapping(64513); - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Fr(Wrapping); - -impl Default for Fr { - fn default() -> Self { - ::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 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>(iter: I) -> Self { - iter.fold(Self::zero(), ::std::ops::Add::add) - } -} - -impl<'r> Sum<&'r Fr> for Fr { - fn sum>(iter: I) -> Self { - iter.fold(Self::zero(), ::std::ops::Add::add) - } -} - -impl Neg for Fr { - type Output = Self; - - fn neg(mut self) -> Self { - if !::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 for Fr { - type Output = u64; - - fn bitand(self, rhs: u64) -> u64 { - (self.0).0 as u64 & rhs - } -} - -impl Shr 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(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 { - if ::is_zero(self) { - CtOption::new(::zero(), Choice::from(0)) - } else { - CtOption::new( - self.pow_vartime(&[(MODULUS_R.0 as u64) - 2]), - Choice::from(1), - ) - } - } - - fn sqrt(&self) -> CtOption { - // 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 != ::one() { - let mut i = 1; - { - let mut t2i = t.square(); - loop { - if t2i == ::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 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 { - 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 = ::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(rng: &mut R) -> Self { - ::random(rng) - } - - fn identity() -> Self { - ::zero() - } - - fn generator() -> Self { - ::one() - } - - fn is_identity(&self) -> Choice { - Choice::from(if ::is_zero(self) { 1 } else { 0 }) - } - - fn double(&self) -> Self { - ::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 { - ::zero() - } - - fn generator() -> Self { - ::one() - } - - fn is_identity(&self) -> Choice { - Choice::from(if ::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 { - unimplemented!() - } - - fn from_bytes_unchecked(_bytes: &Self::Repr) -> CtOption { - unimplemented!() - } - - fn to_bytes(&self) -> Self::Repr { - unimplemented!() - } -} - -impl UncompressedEncoding for Fr { - type Uncompressed = FakePoint; - - fn from_uncompressed(_bytes: &Self::Uncompressed) -> CtOption { - unimplemented!() - } - - fn from_uncompressed_unchecked(_bytes: &Self::Uncompressed) -> CtOption { - 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) - } -} diff --git a/bellman/src/groth16/tests/mod.rs b/bellman/src/groth16/tests/mod.rs deleted file mode 100644 index 371f7c16f..000000000 --- a/bellman/src/groth16/tests/mod.rs +++ /dev/null @@ -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 { - a: Option, - b: Option, - _marker: PhantomData, -} - -impl Circuit for XORDemo { - fn synthesize>(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::(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. = 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::>(); - let v_i = [0, 0, 60619, 30791] - .iter() - .map(|e| Fr::from_str(&format!("{}", e)).unwrap()) - .collect::>(); - let w_i = [0, 23320, 41193, 41193] - .iter() - .map(|e| Fr::from_str(&format!("{}", e)).unwrap()) - .collect::>(); - - for (u, a) in u_i.iter().zip(¶ms.a[..]) { - assert_eq!(u, a); - } - - for (v, b) in v_i - .iter() - .filter(|&&e| e != Fr::zero()) - .zip(¶ms.b_g1[..]) - { - assert_eq!(v, b); - } - - for (v, b) in v_i - .iter() - .filter(|&&e| e != Fr::zero()) - .zip(¶ms.b_g2[..]) - { - assert_eq!(v, b); - } - - for i in 0..4 { - let mut tmp1 = beta; - tmp1.mul_assign(&u_i[i]); - - let mut tmp2 = alpha; - tmp2.mul_assign(&v_i[i]); - - tmp1.add_assign(&tmp2); - tmp1.add_assign(&w_i[i]); - - if i < 2 { - // Check the correctness of the IC query elements - tmp1.mul_assign(&gamma_inverse); - - assert_eq!(tmp1, params.vk.ic[i]); - } else { - // Check the correctness of the L query elements - tmp1.mul_assign(&delta_inverse); - - assert_eq!(tmp1, params.l[i - 2]); - } - } - - // Check consistency of the other elements - assert_eq!(alpha, params.vk.alpha_g1); - assert_eq!(beta, params.vk.beta_g1); - assert_eq!(beta, params.vk.beta_g2); - assert_eq!(gamma, params.vk.gamma_g2); - assert_eq!(delta, params.vk.delta_g1); - assert_eq!(delta, params.vk.delta_g2); - - let pvk = prepare_verifying_key(¶ms.vk); - - let r = Fr::from_str("27134").unwrap(); - let s = Fr::from_str("17146").unwrap(); - - let proof = { - let c = XORDemo { - a: Some(true), - b: Some(false), - _marker: PhantomData, - }; - - create_proof(c, ¶ms, r, s).unwrap() - }; - - // A(x) = - // a_0 * (44865*x^7 + 56449*x^6 + 44865*x^5 + 8064*x^4 + 3520*x^3 + 56449*x^2 + 3520*x + 40321) + - // a_1 * (8064*x^7 + 56449*x^6 + 8064*x^5 + 56449*x^4 + 8064*x^3 + 56449*x^2 + 8064*x + 56449) + - // a_2 * (16983*x^7 + 24192*x^6 + 63658*x^5 + 56449*x^4 + 16983*x^3 + 24192*x^2 + 63658*x + 56449) + - // a_3 * (5539*x^7 + 27797*x^6 + 6045*x^5 + 56449*x^4 + 58974*x^3 + 36716*x^2 + 58468*x + 8064) + - { - // proof A = alpha + A(tau) + delta * r - let mut expected_a = delta; - expected_a.mul_assign(&r); - expected_a.add_assign(&alpha); - expected_a.add_assign(&u_i[0]); // a_0 = 1 - expected_a.add_assign(&u_i[1]); // a_1 = 1 - expected_a.add_assign(&u_i[2]); // a_2 = 1 - // a_3 = 0 - assert_eq!(proof.a, expected_a); - } - - // B(x) = - // a_0 * (0) + - // a_1 * (0) + - // a_2 * (56449*x^7 + 56449*x^6 + 56449*x^5 + 56449*x^4 + 56449*x^3 + 56449*x^2 + 56449*x + 56449) + - // a_3 * (31177*x^7 + 44780*x^6 + 21752*x^5 + 42255*x^3 + 35861*x^2 + 33842*x + 48385) - { - // proof B = beta + B(tau) + delta * s - let mut expected_b = delta; - expected_b.mul_assign(&s); - expected_b.add_assign(&beta); - expected_b.add_assign(&v_i[0]); // a_0 = 1 - expected_b.add_assign(&v_i[1]); // a_1 = 1 - expected_b.add_assign(&v_i[2]); // a_2 = 1 - // a_3 = 0 - assert_eq!(proof.b, expected_b); - } - - // C(x) = - // a_0 * (0) + - // a_1 * (27797*x^7 + 56449*x^6 + 36716*x^5 + 8064*x^4 + 27797*x^3 + 56449*x^2 + 36716*x + 8064) + - // a_2 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) + - // a_3 * (36716*x^7 + 8064*x^6 + 27797*x^5 + 56449*x^4 + 36716*x^3 + 8064*x^2 + 27797*x + 56449) - // - // If A * B = C at each point in the domain, then the following polynomial... - // P(x) = A(x) * B(x) - C(x) - // = 49752*x^14 + 13914*x^13 + 29243*x^12 + 27227*x^11 + 62362*x^10 + 35703*x^9 + 4032*x^8 + 14761*x^6 + 50599*x^5 + 35270*x^4 + 37286*x^3 + 2151*x^2 + 28810*x + 60481 - // - // ... should be divisible by t(x), producing the quotient polynomial: - // h(x) = P(x) / t(x) - // = 49752*x^6 + 13914*x^5 + 29243*x^4 + 27227*x^3 + 62362*x^2 + 35703*x + 4032 - { - let mut expected_c = Fr::zero(); - - // A * s - let mut tmp = proof.a; - tmp.mul_assign(&s); - expected_c.add_assign(&tmp); - - // B * r - let mut tmp = proof.b; - tmp.mul_assign(&r); - expected_c.add_assign(&tmp); - - // delta * r * s - let mut tmp = delta; - tmp.mul_assign(&r); - tmp.mul_assign(&s); - expected_c.sub_assign(&tmp); - - // L query answer - // a_2 = 1, a_3 = 0 - expected_c.add_assign(¶ms.l[0]); - - // H query answer - for (i, coeff) in [5040, 11763, 10755, 63633, 128, 9747, 8739] - .iter() - .enumerate() - { - let coeff = Fr::from_str(&format!("{}", coeff)).unwrap(); - - let mut tmp = params.h[i]; - tmp.mul_assign(&coeff); - expected_c.add_assign(&tmp); - } - - assert_eq!(expected_c, proof.c); - } - - assert!(verify_proof(&pvk, &proof, &[Fr::one()]).is_ok()); -} diff --git a/bellman/src/groth16/verifier.rs b/bellman/src/groth16/verifier.rs deleted file mode 100644 index 43c69cb66..000000000 --- a/bellman/src/groth16/verifier.rs +++ /dev/null @@ -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(vk: &VerifyingKey) -> PreparedVerifyingKey { - 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, - proof: &Proof, - 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) - } -} diff --git a/bellman/src/lib.rs b/bellman/src/lib.rs deleted file mode 100644 index 9eb44dd1d..000000000 --- a/bellman/src/lib.rs +++ /dev/null @@ -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>( -//! mut cs: CS, -//! data: &[Boolean], -//! ) -> Result, 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 Circuit for MyCircuit { -//! fn synthesize>(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::, _>>()?; -//! -//! // 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::(c, &mut OsRng).unwrap() -//! }; -//! -//! // Prepare the verification key (for proof verification). -//! let pvk = groth16::prepare_verifying_key(¶ms.vk); -//! -//! // Pick a preimage and compute its hash. -//! let preimage = [42; 80]; -//! let hash = Sha256::digest(&Sha256::digest(&preimage)); -//! -//! // Create an instance of our circuit (with the preimage as a witness). -//! let c = MyCircuit { -//! preimage: Some(preimage), -//! }; -//! -//! // Create a Groth16 proof with our parameters. -//! let proof = groth16::create_random_proof(c, ¶ms, &mut OsRng).unwrap(); -//! -//! // Pack the hash as inputs for proof verification. -//! let hash_bits = multipack::bytes_to_bits_le(&hash); -//! let inputs = multipack::compute_multipacking(&hash_bits); -//! -//! // Check the proof! -//! assert!(groth16::verify_proof(&pvk, &proof, &inputs).is_ok()); -//! ``` -//! -//! # Roadmap -//! -//! `bellman` is being refactored into a generic proving library. Currently it -//! is pairing-specific, and different types of proving systems need to be -//! implemented as sub-modules. After the refactor, `bellman` will be generic -//! using the [`ff`] and [`group`] crates, while specific proving systems will -//! be separate crates that pull in the dependencies they require. - -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] - -pub mod domain; -pub mod gadgets; -#[cfg(feature = "groth16")] -pub mod groth16; -pub mod multicore; -mod multiexp; - -use ff::PrimeField; - -use std::error::Error; -use std::fmt; -use std::io; -use std::marker::PhantomData; -use std::ops::{Add, Sub}; - -/// Computations are expressed in terms of arithmetic circuits, in particular -/// rank-1 quadratic constraint systems. The `Circuit` trait represents a -/// circuit that can be synthesized. The `synthesize` method is called during -/// CRS generation and during proving. -pub trait Circuit { - /// Synthesize the circuit into a rank-1 quadratic constraint system - fn synthesize>(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(Vec<(Variable, Scalar)>); - -impl AsRef<[(Variable, Scalar)]> for LinearCombination { - fn as_ref(&self) -> &[(Variable, Scalar)] { - &self.0 - } -} - -impl LinearCombination { - pub fn zero() -> LinearCombination { - LinearCombination(vec![]) - } -} - -impl Add<(Scalar, Variable)> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, (coeff, var): (Scalar, Variable)) -> LinearCombination { - self.0.push((var, coeff)); - - self - } -} - -impl Sub<(Scalar, Variable)> for LinearCombination { - type Output = LinearCombination; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, (coeff, var): (Scalar, Variable)) -> LinearCombination { - self + (coeff.neg(), var) - } -} - -impl Add for LinearCombination { - type Output = LinearCombination; - - fn add(self, other: Variable) -> LinearCombination { - self + (Scalar::one(), other) - } -} - -impl Sub for LinearCombination { - type Output = LinearCombination; - - fn sub(self, other: Variable) -> LinearCombination { - self - (Scalar::one(), other) - } -} - -impl<'a, Scalar: PrimeField> Add<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn add(mut self, other: &'a LinearCombination) -> LinearCombination { - for s in &other.0 { - self = self + (s.1, s.0); - } - - self - } -} - -impl<'a, Scalar: PrimeField> Sub<&'a LinearCombination> for LinearCombination { - type Output = LinearCombination; - - fn sub(mut self, other: &'a LinearCombination) -> LinearCombination { - for s in &other.0 { - self = self - (s.1, s.0); - } - - self - } -} - -impl<'a, Scalar: PrimeField> Add<(Scalar, &'a LinearCombination)> - for LinearCombination -{ - type Output = LinearCombination; - - fn add( - mut self, - (coeff, other): (Scalar, &'a LinearCombination), - ) -> LinearCombination { - 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)> - for LinearCombination -{ - type Output = LinearCombination; - - fn sub( - mut self, - (coeff, other): (Scalar, &'a LinearCombination), - ) -> LinearCombination { - 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 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: Sized { - /// Represents the type of the "root" of this constraint system - /// so that nested namespaces can minimize indirection. - type Root: ConstraintSystem; - - /// 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(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into; - - /// Allocate a public variable in the constraint system. The provided function is used to - /// determine the assignment of the variable. - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into; - - /// 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(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination; - - /// Create a new (sub)namespace and enter into it. Not intended - /// for downstream use; use `namespace` instead. - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - 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(&mut self, name_fn: N) -> Namespace<'_, Scalar, Self::Root> - where - NR: Into, - 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>( - &'a mut CS, - PhantomData, -); - -impl<'cs, Scalar: PrimeField, CS: ConstraintSystem> ConstraintSystem - for Namespace<'cs, Scalar, CS> -{ - type Root = CS::Root; - - fn one() -> Variable { - CS::one() - } - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.0.alloc(annotation, f) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - self.0.alloc_input(annotation, f) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - 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(&mut self, _: N) - where - NR: Into, - 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> Drop for Namespace<'a, Scalar, CS> { - fn drop(&mut self) { - self.get_root().pop_namespace() - } -} - -/// Convenience implementation of ConstraintSystem for mutable references to -/// constraint systems. -impl<'cs, Scalar: PrimeField, CS: ConstraintSystem> ConstraintSystem - for &'cs mut CS -{ - type Root = CS::Root; - - fn one() -> Variable { - CS::one() - } - - fn alloc(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - (**self).alloc(annotation, f) - } - - fn alloc_input(&mut self, annotation: A, f: F) -> Result - where - F: FnOnce() -> Result, - A: FnOnce() -> AR, - AR: Into, - { - (**self).alloc_input(annotation, f) - } - - fn enforce(&mut self, annotation: A, a: LA, b: LB, c: LC) - where - A: FnOnce() -> AR, - AR: Into, - LA: FnOnce(LinearCombination) -> LinearCombination, - LB: FnOnce(LinearCombination) -> LinearCombination, - LC: FnOnce(LinearCombination) -> LinearCombination, - { - (**self).enforce(annotation, a, b, c) - } - - fn push_namespace(&mut self, name_fn: N) - where - NR: Into, - 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() - } -} diff --git a/bellman/src/multicore.rs b/bellman/src/multicore.rs deleted file mode 100644 index ba69b5f33..000000000 --- a/bellman/src/multicore.rs +++ /dev/null @@ -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(&self, f: F) -> WorkerFuture - 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 { - future: CpuFuture, - } - - impl Future for WorkerFuture { - type Item = T; - type Error = E; - - fn poll(&mut self) -> Poll { - 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(&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(&self, elements: usize, f: F) -> R - where - F: FnOnce(&DummyScope, usize) -> R, - { - f(&DummyScope, elements) - } - } - - pub struct WorkerFuture { - future: future::FutureResult, - } - - impl Future for WorkerFuture { - type Item = T; - type Error = E; - - fn poll(&mut self) -> Poll { - self.future.poll() - } - } - - pub struct DummyScope; - - impl DummyScope { - pub fn spawn(&self, f: F) { - f(self); - } - } -} - -pub use self::implementation::*; diff --git a/bellman/src/multiexp.rs b/bellman/src/multiexp.rs deleted file mode 100644 index b70a2ea64..000000000 --- a/bellman/src/multiexp.rs +++ /dev/null @@ -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: Send + Sync + 'static + Clone { - type Source: Source; - - fn new(self) -> Self::Source; -} - -/// A source of bases, like an iterator. -pub trait Source { - 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::Affine>>( - &mut self, - source: &mut S, - ) -> Result<(), SynthesisError> { - AddAssign::<&::Affine>::add_assign(self, source.next()?); - Ok(()) - } -} -impl AddAssignFromSource for G where G: PrimeCurve {} - -impl SourceBuilder for (Arc>, usize) { - type Source = (Arc>, usize); - - fn new(self) -> (Arc>, usize) { - (self.0.clone(), self.1) - } -} - -impl Source for (Arc>, 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; - - fn iter(self) -> Self::Iter; - fn get_query_size(self) -> Option; -} - -#[derive(Clone)] -pub struct FullDensity; - -impl AsRef for FullDensity { - fn as_ref(&self) -> &FullDensity { - self - } -} - -impl<'a> QueryDensity for &'a FullDensity { - type Iter = iter::Repeat; - - fn iter(self) -> Self::Iter { - iter::repeat(true) - } - - fn get_query_size(self) -> Option { - 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 { - 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( - pool: &Worker, - bases: S, - density_map: D, - exponents: Arc>, - mut skip: u32, - c: u32, - handle_trivial: bool, -) -> Box> -where - for<'a> &'a Q: QueryDensity, - D: Send + Sync + 'static + Clone + AsRef, - G: PrimeCurve, - S: SourceBuilder<::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(); - ::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( - pool: &Worker, - bases: S, - density_map: D, - exponents: Arc>, -) -> Box> -where - for<'a> &'a Q: QueryDensity, - D: Send + Sync + 'static + Clone + AsRef, - G: PrimeCurve, - S: SourceBuilder<::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( - bases: Arc::Affine>>, - exponents: Arc>, - ) -> 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::>(), - ); - let g = Arc::new( - (0..SAMPLES) - .map(|_| ::G1::random(rng).to_affine()) - .collect::>(), - ); - - let naive: ::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); -} diff --git a/bellman/tests/mimc.rs b/bellman/tests/mimc.rs deleted file mode 100644 index e8cb9219b..000000000 --- a/bellman/tests/mimc.rs +++ /dev/null @@ -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(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, - xr: Option, - 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 for MiMCDemo<'a, Scalar> { - fn synthesize>(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::>(); - - println!("Creating parameters..."); - - // Create parameters for our circuit - let params = { - let c = MiMCDemo { - xl: None, - xr: None, - constants: &constants, - }; - - generate_random_parameters::(c, rng).unwrap() - }; - - // Prepare the verification key (for proof verification) - let pvk = prepare_verifying_key(¶ms.vk); - - println!("Creating proofs..."); - - // Let's benchmark stuff! - const SAMPLES: u32 = 50; - let mut total_proving = Duration::new(0, 0); - let mut total_verifying = Duration::new(0, 0); - - // Just a place to put the proof data, so we can - // benchmark deserialization. - let mut proof_vec = vec![]; - - for _ in 0..SAMPLES { - // Generate a random preimage and compute the image - let xl = Scalar::random(rng); - let xr = Scalar::random(rng); - let image = mimc(xl, xr, &constants); - - proof_vec.truncate(0); - - let start = Instant::now(); - { - // Create an instance of our circuit (with the - // witness) - let c = MiMCDemo { - xl: Some(xl), - xr: Some(xr), - constants: &constants, - }; - - // Create a groth16 proof with our parameters. - let proof = create_random_proof(c, ¶ms, rng).unwrap(); - - proof.write(&mut proof_vec).unwrap(); - } - - total_proving += start.elapsed(); - - let start = Instant::now(); - let proof = Proof::read(&proof_vec[..]).unwrap(); - // Check the proof - assert!(verify_proof(&pvk, &proof, &[image]).is_ok()); - total_verifying += start.elapsed(); - } - let proving_avg = total_proving / SAMPLES; - let proving_avg = - proving_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (proving_avg.as_secs() as f64); - - let verifying_avg = total_verifying / SAMPLES; - let verifying_avg = - verifying_avg.subsec_nanos() as f64 / 1_000_000_000f64 + (verifying_avg.as_secs() as f64); - - println!("Average proving time: {:?} seconds", proving_avg); - println!("Average verifying time: {:?} seconds", verifying_avg); -} diff --git a/bls12_381/.github/workflows/ci.yml b/bls12_381/.github/workflows/ci.yml deleted file mode 100644 index 1388989f0..000000000 --- a/bls12_381/.github/workflows/ci.yml +++ /dev/null @@ -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 diff --git a/bls12_381/.gitignore b/bls12_381/.gitignore deleted file mode 100644 index 2f88dbac5..000000000 --- a/bls12_381/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock \ No newline at end of file diff --git a/bls12_381/COPYRIGHT b/bls12_381/COPYRIGHT deleted file mode 100644 index 7764b866e..000000000 --- a/bls12_381/COPYRIGHT +++ /dev/null @@ -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. diff --git a/bls12_381/Cargo.toml b/bls12_381/Cargo.toml deleted file mode 100644 index 441140142..000000000 --- a/bls12_381/Cargo.toml +++ /dev/null @@ -1,62 +0,0 @@ -[package] -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -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 = [] diff --git a/bls12_381/LICENSE-APACHE b/bls12_381/LICENSE-APACHE deleted file mode 100644 index 16fe87b06..000000000 --- a/bls12_381/LICENSE-APACHE +++ /dev/null @@ -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. diff --git a/bls12_381/LICENSE-MIT b/bls12_381/LICENSE-MIT deleted file mode 100644 index 31aa79387..000000000 --- a/bls12_381/LICENSE-MIT +++ /dev/null @@ -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. diff --git a/bls12_381/README.md b/bls12_381/README.md deleted file mode 100644 index 70a238e50..000000000 --- a/bls12_381/README.md +++ /dev/null @@ -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)2(z4 - z2 + 1) / 3 + z - * = `0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab` -* q = z4 - z2 + 1 - * = `0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` - -... yielding two **source groups** G1 and G2, each of 255-bit prime order `q`, such that an efficiently computable non-degenerate bilinear pairing function `e` exists into a third **target group** GT. Specifically, G1 is the `q`-order subgroup of E(Fp) : y2 = x3 + 4 and G2 is the `q`-order subgroup of E'(Fp2) : y2 = x3 + 4(u + 1) where the extension field Fp2 is defined as Fp(u) / (u2 + 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 232 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 G2 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. diff --git a/bls12_381/RELEASES.md b/bls12_381/RELEASES.md deleted file mode 100644 index 5f314c437..000000000 --- a/bls12_381/RELEASES.md +++ /dev/null @@ -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. diff --git a/bls12_381/benches/groups.rs b/bls12_381/benches/groups.rs deleted file mode 100644 index 87c80d029..000000000 --- a/bls12_381/benches/groups.rs +++ /dev/null @@ -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); diff --git a/bls12_381/katex-header.html b/bls12_381/katex-header.html deleted file mode 100644 index 98e85904f..000000000 --- a/bls12_381/katex-header.html +++ /dev/null @@ -1,15 +0,0 @@ - - - - \ No newline at end of file diff --git a/bls12_381/rust-toolchain b/bls12_381/rust-toolchain deleted file mode 100644 index d70132e1d..000000000 --- a/bls12_381/rust-toolchain +++ /dev/null @@ -1 +0,0 @@ -1.36.0 \ No newline at end of file diff --git a/bls12_381/src/fp.rs b/bls12_381/src/fp.rs deleted file mode 100644 index 336955451..000000000 --- a/bls12_381/src/fp.rs +++ /dev/null @@ -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 { - 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(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 { - // 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 { - // 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 - // . - - 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() - )); -} diff --git a/bls12_381/src/fp12.rs b/bls12_381/src/fp12.rs deleted file mode 100644 index f6177cc80..000000000 --- a/bls12_381/src/fp12.rs +++ /dev/null @@ -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 for Fp12 { - fn from(f: Fp) -> Fp12 { - Fp12 { - c0: Fp6::from(f), - c1: Fp6::zero(), - } - } -} - -impl From for Fp12 { - fn from(f: Fp2) -> Fp12 { - Fp12 { - c0: Fp6::from(f), - c1: Fp6::zero(), - } - } -} - -impl From 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(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.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() - ); -} diff --git a/bls12_381/src/fp2.rs b/bls12_381/src/fp2.rs deleted file mode 100644 index e9d9275a4..000000000 --- a/bls12_381/src/fp2.rs +++ /dev/null @@ -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 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(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 { - // 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 { - // 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() - )); -} diff --git a/bls12_381/src/fp6.rs b/bls12_381/src/fp6.rs deleted file mode 100644 index 9df150f65..000000000 --- a/bls12_381/src/fp6.rs +++ /dev/null @@ -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 for Fp6 { - fn from(f: Fp) -> Fp6 { - Fp6 { - c0: Fp2::from(f), - c1: Fp2::zero(), - c2: Fp2::zero(), - } - } -} - -impl From 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(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 { - 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()); -} diff --git a/bls12_381/src/g1.rs b/bls12_381/src/g1.rs deleted file mode 100644 index d30f88240..000000000 --- a/bls12_381/src/g1.rs +++ /dev/null @@ -1,1678 +0,0 @@ -//! This module provides an implementation of the $\mathbb{G}_1$ group of BLS12-381. - -use core::borrow::Borrow; -use core::fmt; -use core::iter::Sum; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use group::{ - prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, - Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, -}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::fp::Fp; -use crate::Scalar; - -/// This is an element of $\mathbb{G}_1$ represented in the affine coordinate space. -/// It is ideal to keep elements in this representation to reduce memory usage and -/// improve performance through the use of mixed curve model arithmetic. -/// -/// Values of `G1Affine` are guaranteed to be in the $q$-order subgroup unless an -/// "unchecked" API was misused. -#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] -#[derive(Copy, Clone, Debug)] -pub struct G1Affine { - pub(crate) x: Fp, - pub(crate) y: Fp, - infinity: Choice, -} - -impl Default for G1Affine { - fn default() -> G1Affine { - G1Affine::identity() - } -} - -impl fmt::Display for G1Affine { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl<'a> From<&'a G1Projective> for G1Affine { - fn from(p: &'a G1Projective) -> G1Affine { - let zinv = p.z.invert().unwrap_or(Fp::zero()); - let zinv2 = zinv.square(); - let x = p.x * zinv2; - let zinv3 = zinv2 * zinv; - let y = p.y * zinv3; - - let tmp = G1Affine { - x, - y, - infinity: Choice::from(0u8), - }; - - G1Affine::conditional_select(&tmp, &G1Affine::identity(), zinv.is_zero()) - } -} - -impl From for G1Affine { - fn from(p: G1Projective) -> G1Affine { - G1Affine::from(&p) - } -} - -impl ConstantTimeEq for G1Affine { - fn ct_eq(&self, other: &Self) -> Choice { - // The only cases in which two points are equal are - // 1. infinity is set on both - // 2. infinity is not set on both, and their coordinates are equal - - (self.infinity & other.infinity) - | ((!self.infinity) - & (!other.infinity) - & self.x.ct_eq(&other.x) - & self.y.ct_eq(&other.y)) - } -} - -impl ConditionallySelectable for G1Affine { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G1Affine { - x: Fp::conditional_select(&a.x, &b.x, choice), - y: Fp::conditional_select(&a.y, &b.y, choice), - infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), - } - } -} - -impl Eq for G1Affine {} -impl PartialEq for G1Affine { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G1Affine { - type Output = G1Affine; - - #[inline] - fn neg(self) -> G1Affine { - G1Affine { - x: self.x, - y: Fp::conditional_select(&-self.y, &Fp::one(), self.infinity), - infinity: self.infinity, - } - } -} - -impl Neg for G1Affine { - type Output = G1Affine; - - #[inline] - fn neg(self) -> G1Affine { - -&self - } -} - -impl<'a, 'b> Add<&'b G1Projective> for &'a G1Affine { - type Output = G1Projective; - - #[inline] - fn add(self, rhs: &'b G1Projective) -> G1Projective { - rhs.add_mixed(self) - } -} - -impl<'a, 'b> Add<&'b G1Affine> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn add(self, rhs: &'b G1Affine) -> G1Projective { - self.add_mixed(rhs) - } -} - -impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Affine { - type Output = G1Projective; - - #[inline] - fn sub(self, rhs: &'b G1Projective) -> G1Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Sub<&'b G1Affine> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn sub(self, rhs: &'b G1Affine) -> G1Projective { - self + (-rhs) - } -} - -impl Sum for G1Projective -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl_binops_additive!(G1Projective, G1Affine); -impl_binops_additive_specify_output!(G1Affine, G1Projective, G1Projective); - -const B: Fp = 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, -]); - -impl G1Affine { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G1Affine { - G1Affine { - x: Fp::zero(), - y: Fp::one(), - infinity: Choice::from(1u8), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G1Affine { - G1Affine { - x: Fp::from_raw_unchecked([ - 0x5cb3_8790_fd53_0c16, - 0x7817_fc67_9976_fff5, - 0x154f_95c7_143b_a1c1, - 0xf0ae_6acd_f3d0_e747, - 0xedce_6ecc_21db_f440, - 0x1201_7741_9e0b_fb75, - ]), - y: Fp::from_raw_unchecked([ - 0xbaac_93d5_0ce7_2271, - 0x8c22_631a_7918_fd8e, - 0xdd59_5f13_5707_25ce, - 0x51ac_5829_5040_5194, - 0x0e1c_8c3f_ad00_59c0, - 0x0bbc_3efc_5008_a26a, - ]), - infinity: Choice::from(0u8), - } - } - - /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_compressed(&self) -> [u8; 48] { - // Strictly speaking, self.x is zero already when self.infinity is true, but - // to guard against implementation mistakes we do not assume this. - let mut res = Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes(); - - // This point is in compressed form, so we set the most significant bit. - res[0] |= 1u8 << 7; - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= u8::conditional_select( - &0u8, - &(1u8 << 5), - (!self.infinity) & self.y.lexicographically_largest(), - ); - - res - } - - /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_uncompressed(&self) -> [u8; 96] { - let mut res = [0; 96]; - - res[0..48].copy_from_slice( - &Fp::conditional_select(&self.x, &Fp::zero(), self.infinity).to_bytes()[..], - ); - res[48..96].copy_from_slice( - &Fp::conditional_select(&self.y, &Fp::zero(), self.infinity).to_bytes()[..], - ); - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - res - } - - /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_uncompressed(bytes: &[u8; 96]) -> CtOption { - Self::from_uncompressed_unchecked(bytes) - .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is on the curve and not checking if it 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 `from_uncompressed()` instead. - pub fn from_uncompressed_unchecked(bytes: &[u8; 96]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let x = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - - // Attempt to obtain the y-coordinate - let y = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[48..96]); - - Fp::from_bytes(&tmp) - }; - - x.and_then(|x| { - y.and_then(|y| { - // Create a point representing this value - let p = G1Affine::conditional_select( - &G1Affine { - x, - y, - infinity: infinity_flag_set, - }, - &G1Affine::identity(), - infinity_flag_set, - ); - - CtOption::new( - p, - // If the infinity flag is set, the x and y coordinates should have been zero. - ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & - // The compression flag should not have been set, as this is an uncompressed element - (!compression_flag_set) & - // The sort flag should not have been set, as this is an uncompressed element - (!sort_flag_set), - ) - }) - }) - } - - /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_compressed(bytes: &[u8; 48]) -> CtOption { - // We already know the point is on the curve because this is established - // by the y-coordinate recovery procedure in from_compressed_unchecked(). - - Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) - } - - /// 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 `from_compressed()` instead. - pub fn from_compressed_unchecked(bytes: &[u8; 48]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let x = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - - x.and_then(|x| { - // If the infinity flag is set, return the value assuming - // the x-coordinate is zero and the sort bit is not set. - // - // Otherwise, return a recovered point (assuming the correct - // y-coordinate can be found) so long as the infinity flag - // was not set. - CtOption::new( - G1Affine::identity(), - infinity_flag_set & // Infinity flag should be set - compression_flag_set & // Compression flag should be set - (!sort_flag_set) & // Sort flag should not be set - x.is_zero(), // The x-coordinate should be zero - ) - .or_else(|| { - // Recover a y-coordinate given x by y = sqrt(x^3 + 4) - ((x.square() * x) + B).sqrt().and_then(|y| { - // Switch to the correct y-coordinate if necessary. - let y = Fp::conditional_select( - &y, - &-y, - y.lexicographically_largest() ^ sort_flag_set, - ); - - CtOption::new( - G1Affine { - x, - y, - infinity: infinity_flag_set, - }, - (!infinity_flag_set) & // Infinity flag should not be set - compression_flag_set, // Compression flag should be set - ) - }) - }) - }) - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.infinity - } - - /// Returns true if this point is free of an $h$-torsion component, and so it - /// exists within the $q$-order subgroup $\mathbb{G}_1$. This should always return true - /// unless an "unchecked" API was used. - pub fn is_torsion_free(&self) -> Choice { - const FQ_MODULUS_BYTES: [u8; 32] = [ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, - ]; - - // Clear the r-torsion from the point and check if it is the identity - G1Projective::from(*self) - .multiply(&FQ_MODULUS_BYTES) - .is_identity() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // y^2 - x^3 ?= 4 - (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity - } -} - -/// This is an element of $\mathbb{G}_1$ represented in the projective coordinate space. -#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] -#[derive(Copy, Clone, Debug)] -pub struct G1Projective { - x: Fp, - y: Fp, - z: Fp, -} - -impl Default for G1Projective { - fn default() -> G1Projective { - G1Projective::identity() - } -} - -impl fmt::Display for G1Projective { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl<'a> From<&'a G1Affine> for G1Projective { - fn from(p: &'a G1Affine) -> G1Projective { - G1Projective { - x: p.x, - y: p.y, - z: Fp::conditional_select(&Fp::one(), &Fp::zero(), p.infinity), - } - } -} - -impl From for G1Projective { - fn from(p: G1Affine) -> G1Projective { - G1Projective::from(&p) - } -} - -impl ConstantTimeEq for G1Projective { - fn ct_eq(&self, other: &Self) -> Choice { - // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? - - let z = other.z.square(); - let x1 = self.x * z; - let z = z * other.z; - let y1 = self.y * z; - let z = self.z.square(); - let x2 = other.x * z; - let z = z * self.z; - let y2 = other.y * z; - - let self_is_zero = self.z.is_zero(); - let other_is_zero = other.z.is_zero(); - - (self_is_zero & other_is_zero) // Both point at infinity - | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) - // Neither point at infinity, coordinates are the same - } -} - -impl ConditionallySelectable for G1Projective { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G1Projective { - x: Fp::conditional_select(&a.x, &b.x, choice), - y: Fp::conditional_select(&a.y, &b.y, choice), - z: Fp::conditional_select(&a.z, &b.z, choice), - } - } -} - -impl Eq for G1Projective {} -impl PartialEq for G1Projective { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn neg(self) -> G1Projective { - G1Projective { - x: self.x, - y: -self.y, - z: self.z, - } - } -} - -impl Neg for G1Projective { - type Output = G1Projective; - - #[inline] - fn neg(self) -> G1Projective { - -&self - } -} - -impl<'a, 'b> Add<&'b G1Projective> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn add(self, rhs: &'b G1Projective) -> G1Projective { - self.add(rhs) - } -} - -impl<'a, 'b> Sub<&'b G1Projective> for &'a G1Projective { - type Output = G1Projective; - - #[inline] - fn sub(self, rhs: &'b G1Projective) -> G1Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G1Projective { - type Output = G1Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - self.multiply(&other.to_bytes()) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G1Affine { - type Output = G1Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - G1Projective::from(self).multiply(&other.to_bytes()) - } -} - -impl_binops_additive!(G1Projective, G1Projective); -impl_binops_multiplicative!(G1Projective, Scalar); -impl_binops_multiplicative_mixed!(G1Affine, Scalar, G1Projective); - -impl G1Projective { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G1Projective { - G1Projective { - x: Fp::zero(), - y: Fp::one(), - z: Fp::zero(), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G1Projective { - G1Projective { - x: Fp::from_raw_unchecked([ - 0x5cb3_8790_fd53_0c16, - 0x7817_fc67_9976_fff5, - 0x154f_95c7_143b_a1c1, - 0xf0ae_6acd_f3d0_e747, - 0xedce_6ecc_21db_f440, - 0x1201_7741_9e0b_fb75, - ]), - y: Fp::from_raw_unchecked([ - 0xbaac_93d5_0ce7_2271, - 0x8c22_631a_7918_fd8e, - 0xdd59_5f13_5707_25ce, - 0x51ac_5829_5040_5194, - 0x0e1c_8c3f_ad00_59c0, - 0x0bbc_3efc_5008_a26a, - ]), - z: Fp::one(), - } - } - - /// Computes the doubling of this point. - pub fn double(&self) -> G1Projective { - // http://www.hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - // - // There are no points of order 2. - - let a = self.x.square(); - let b = self.y.square(); - let c = b.square(); - let d = self.x + b; - let d = d.square(); - let d = d - a - c; - let d = d + d; - let e = a + a + a; - let f = e.square(); - let z3 = self.z * self.y; - let z3 = z3 + z3; - let x3 = f - (d + d); - let c = c + c; - let c = c + c; - let c = c + c; - let y3 = e * (d - x3) - c; - - let tmp = G1Projective { - x: x3, - y: y3, - z: z3, - }; - - G1Projective::conditional_select(&tmp, &G1Projective::identity(), self.is_identity()) - } - - /// Adds this point to another point. - pub fn add(&self, rhs: &G1Projective) -> G1Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G1Projective::conditional_select(self, rhs, f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let z = rhs.z.square(); - let u1 = self.x * z; - let z = z * rhs.z; - let s1 = self.y * z; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G1Projective { - x: x3, - y: y3, - z: z3, - }; - - G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - /// Adds this point to another point in the affine model. - pub fn add_mixed(&self, rhs: &G1Affine) -> G1Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G1Projective::conditional_select(self, &G1Projective::from(rhs), f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let u1 = self.x; - let s1 = self.y; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G1Projective::conditional_select(&res, &G1Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z; - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G1Projective { - x: x3, - y: y3, - z: z3, - }; - - G1Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - fn multiply(&self, by: &[u8; 32]) -> G1Projective { - let mut acc = G1Projective::identity(); - - // This is a simple double-and-add implementation of point - // 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 by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = G1Projective::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - - /// Multiply `self` by `crate::BLS_X`, using double and add. - fn mul_by_x(&self) -> G1Projective { - let mut xself = G1Projective::identity(); - // NOTE: in BLS12-381 we can just skip the first bit. - let mut x = crate::BLS_X >> 1; - let mut tmp = *self; - while x != 0 { - tmp = tmp.double(); - - if x % 2 == 1 { - xself += tmp; - } - x >>= 1; - } - // finally, flip the sign - if crate::BLS_X_IS_NEGATIVE { - xself = -xself; - } - xself - } - - /// Multiplies by $(1 - z)$, where $z$ is the parameter of BLS12-381, which - /// [suffices to clear](https://ia.cr/2019/403) the cofactor and map - /// elliptic curve points to elements of $\mathbb{G}\_1$. - pub fn clear_cofactor(&self) -> G1Projective { - self - self.mul_by_x() - } - - /// Converts a batch of `G1Projective` elements into `G1Affine` elements. This - /// function will panic if `p.len() != q.len()`. - pub fn batch_normalize(p: &[Self], q: &mut [G1Affine]) { - assert_eq!(p.len(), q.len()); - - let mut acc = Fp::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `x` field of `G1Affine` to store the product - // of previous z-coordinates seen. - q.x = acc; - - // We will end up skipping all identities in p - acc = Fp::conditional_select(&(acc * p.z), &acc, p.is_identity()); - } - - // This is the inverse, as all z-coordinates are nonzero and the ones - // that are not are skipped. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { - let skip = p.is_identity(); - - // Compute tmp = 1/z - let tmp = q.x * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc = Fp::conditional_select(&(acc * p.z), &acc, skip); - - // Set the coordinates to the correct value - let tmp2 = tmp.square(); - let tmp3 = tmp2 * tmp; - - q.x = p.x * tmp2; - q.y = p.y * tmp3; - q.infinity = Choice::from(0u8); - - *q = G1Affine::conditional_select(&q, &G1Affine::identity(), skip); - } - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.z.is_zero() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // Y^2 - X^3 = 4(Z^6) - - (self.y.square() - (self.x.square() * self.x)) - .ct_eq(&((self.z.square() * self.z).square() * B)) - | self.z.is_zero() - } -} - -pub struct G1Compressed([u8; 48]); - -impl fmt::Debug for G1Compressed { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0[..].fmt(f) - } -} - -impl Default for G1Compressed { - fn default() -> Self { - G1Compressed([0; 48]) - } -} - -impl AsRef<[u8]> for G1Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for G1Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -pub struct G1Uncompressed([u8; 96]); - -impl fmt::Debug for G1Uncompressed { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0[..].fmt(f) - } -} - -impl Default for G1Uncompressed { - fn default() -> Self { - G1Uncompressed([0; 96]) - } -} - -impl AsRef<[u8]> for G1Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for G1Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -impl Group for G1Projective { - type Scalar = Scalar; - - fn random(rng: &mut R) -> Self { - loop { - let x = Fp::random(rng); - let flip_sign = rng.next_u32() % 2 != 0; - - // Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4) - let p = ((x.square() * x) + B).sqrt().map(|y| G1Affine { - x, - y: if flip_sign { -y } else { y }, - infinity: 0.into(), - }); - - if p.is_some().into() { - let p = p.unwrap().to_curve().clear_cofactor(); - - if bool::from(!p.is_identity()) { - return p; - } - } - } - } - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - Self::generator() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } -} - -impl WnafGroup for G1Projective { - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - const RECOMMENDATIONS: [usize; 12] = - [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } -} - -impl PrimeGroup for G1Projective {} - -impl Curve for G1Projective { - type AffineRepr = G1Affine; - - fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { - Self::batch_normalize(p, q); - } - - fn to_affine(&self) -> Self::AffineRepr { - self.into() - } -} - -impl PrimeCurve for G1Projective { - type Affine = G1Affine; -} - -impl PrimeCurveAffine for G1Affine { - type Scalar = Scalar; - type Curve = G1Projective; - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - Self::generator() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - fn to_curve(&self) -> Self::Curve { - self.into() - } -} - -impl GroupEncoding for G1Projective { - type Repr = G1Compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - G1Affine::from_bytes(bytes).map(Self::from) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - G1Affine::from_bytes_unchecked(bytes).map(Self::from) - } - - fn to_bytes(&self) -> Self::Repr { - G1Affine::from(self).to_bytes() - } -} - -impl GroupEncoding for G1Affine { - type Repr = G1Compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - Self::from_compressed(&bytes.0) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - Self::from_compressed_unchecked(&bytes.0) - } - - fn to_bytes(&self) -> Self::Repr { - G1Compressed(self.to_compressed()) - } -} - -impl UncompressedEncoding for G1Affine { - type Uncompressed = G1Uncompressed; - - fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { - Self::from_uncompressed(&bytes.0) - } - - fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { - Self::from_uncompressed_unchecked(&bytes.0) - } - - fn to_uncompressed(&self) -> Self::Uncompressed { - G1Uncompressed(self.to_uncompressed()) - } -} - -#[test] -fn test_is_on_curve() { - assert!(bool::from(G1Affine::identity().is_on_curve())); - assert!(bool::from(G1Affine::generator().is_on_curve())); - assert!(bool::from(G1Projective::identity().is_on_curve())); - assert!(bool::from(G1Projective::generator().is_on_curve())); - - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - let gen = G1Affine::generator(); - let mut test = G1Projective { - x: gen.x * (z.square()), - y: gen.y * (z.square() * z), - z, - }; - - assert!(bool::from(test.is_on_curve())); - - test.x = z; - assert!(!bool::from(test.is_on_curve())); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_affine_point_equality() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_point_equality() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - let mut c = G1Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - assert!(bool::from(c.is_on_curve())); - - assert!(a == c); - assert!(b != c); - assert!(c == a); - assert!(c != b); - - c.y = -c.y; - assert!(bool::from(c.is_on_curve())); - - assert!(a != c); - assert!(b != c); - assert!(c != a); - assert!(c != b); - - c.y = -c.y; - c.x = z; - assert!(!bool::from(c.is_on_curve())); - assert!(a != b); - assert!(a != c); - assert!(b != c); -} - -#[test] -fn test_conditionally_select_affine() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(0u8)), a); - assert_eq!(G1Affine::conditional_select(&a, &b, Choice::from(1u8)), b); -} - -#[test] -fn test_conditionally_select_projective() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); - - assert_eq!( - G1Projective::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - G1Projective::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_projective_to_affine() { - let a = G1Projective::generator(); - let b = G1Projective::identity(); - - assert!(bool::from(G1Affine::from(a).is_on_curve())); - assert!(!bool::from(G1Affine::from(a).is_identity())); - assert!(bool::from(G1Affine::from(b).is_on_curve())); - assert!(bool::from(G1Affine::from(b).is_identity())); - - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - let c = G1Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - - assert_eq!(G1Affine::from(c), G1Affine::generator()); -} - -#[test] -fn test_affine_to_projective() { - let a = G1Affine::generator(); - let b = G1Affine::identity(); - - assert!(bool::from(G1Projective::from(a).is_on_curve())); - assert!(!bool::from(G1Projective::from(a).is_identity())); - assert!(bool::from(G1Projective::from(b).is_on_curve())); - assert!(bool::from(G1Projective::from(b).is_identity())); -} - -#[test] -fn test_doubling() { - { - let tmp = G1Projective::identity().double(); - assert!(bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - } - { - let tmp = G1Projective::generator().double(); - assert!(!bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - - assert_eq!( - G1Affine::from(tmp), - G1Affine { - x: Fp::from_raw_unchecked([ - 0x53e9_78ce_58a9_ba3c, - 0x3ea0_583c_4f3d_65f9, - 0x4d20_bb47_f001_2960, - 0xa54c_664a_e5b2_b5d9, - 0x26b5_52a3_9d7e_b21f, - 0x0008_895d_26e6_8785, - ]), - y: Fp::from_raw_unchecked([ - 0x7011_0b32_9829_3940, - 0xda33_c539_3f1f_6afc, - 0xb86e_dfd1_6a5a_a785, - 0xaec6_d1c9_e7b1_c895, - 0x25cf_c2b5_22d1_1720, - 0x0636_1c83_f8d0_9b15, - ]), - infinity: Choice::from(0u8) - } - ); - } -} - -#[test] -fn test_projective_addition() { - { - let a = G1Projective::identity(); - let b = G1Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G1Projective::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::generator().double().double(); // 4P - let b = G1Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G1Projective::generator(); - for _ in 0..5 { - d += G1Projective::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = 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, - ]); - let beta = beta.square(); - let a = G1Projective::generator().double().double(); - let b = G1Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G1Affine::from(c), - G1Affine::from(G1Projective { - x: Fp::from_raw_unchecked([ - 0x29e1_e987_ef68_f2d0, - 0xc5f3_ec53_1db0_3233, - 0xacd6_c4b6_ca19_730f, - 0x18ad_9e82_7bc2_bab7, - 0x46e3_b2c5_785c_c7a9, - 0x07e5_71d4_2d22_ddd6, - ]), - y: Fp::from_raw_unchecked([ - 0x94d1_17a7_e5a5_39e7, - 0x8e17_ef67_3d4b_5d22, - 0x9d74_6aaf_508a_33ea, - 0x8c6d_883d_2516_c9a2, - 0x0bc3_b8d5_fb04_47f7, - 0x07bf_a4c7_210f_4f44, - ]), - z: Fp::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -fn test_mixed_addition() { - { - let a = G1Affine::identity(); - let b = G1Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G1Affine::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Affine::identity(); - let mut b = G1Projective::generator(); - { - let z = Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]); - - b = G1Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G1Projective::generator()); - } - { - let a = G1Projective::generator().double().double(); // 4P - let b = G1Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G1Projective::generator(); - for _ in 0..5 { - d += G1Affine::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = 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, - ]); - let beta = beta.square(); - let a = G1Projective::generator().double().double(); - let b = G1Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - let a = G1Affine::from(a); - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G1Affine::from(c), - G1Affine::from(G1Projective { - x: Fp::from_raw_unchecked([ - 0x29e1_e987_ef68_f2d0, - 0xc5f3_ec53_1db0_3233, - 0xacd6_c4b6_ca19_730f, - 0x18ad_9e82_7bc2_bab7, - 0x46e3_b2c5_785c_c7a9, - 0x07e5_71d4_2d22_ddd6, - ]), - y: Fp::from_raw_unchecked([ - 0x94d1_17a7_e5a5_39e7, - 0x8e17_ef67_3d4b_5d22, - 0x9d74_6aaf_508a_33ea, - 0x8c6d_883d_2516_c9a2, - 0x0bc3_b8d5_fb04_47f7, - 0x07bf_a4c7_210f_4f44, - ]), - z: Fp::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_negation_and_subtraction() { - let a = G1Projective::generator().double(); - assert_eq!(a + (-a), G1Projective::identity()); - assert_eq!(a + (-a), a - a); -} - -#[test] -fn test_affine_negation_and_subtraction() { - let a = G1Affine::generator(); - assert_eq!(G1Projective::from(a) + (-a), G1Projective::identity()); - assert_eq!(G1Projective::from(a) + (-a), G1Projective::from(a) - a); -} - -#[test] -fn test_projective_scalar_multiplication() { - let g = G1Projective::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!((g * a) * b, g * c); -} - -#[test] -fn test_affine_scalar_multiplication() { - let g = G1Affine::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!(G1Affine::from(g * a) * b, g * c); -} - -#[test] -fn test_is_torsion_free() { - let a = G1Affine { - x: Fp::from_raw_unchecked([ - 0x0aba_f895_b97e_43c8, - 0xba4c_6432_eb9b_61b0, - 0x1250_6f52_adfe_307f, - 0x7502_8c34_3933_6b72, - 0x8474_4f05_b8e9_bd71, - 0x113d_554f_b095_54f7, - ]), - y: Fp::from_raw_unchecked([ - 0x73e9_0e88_f5cf_01c0, - 0x3700_7b65_dd31_97e2, - 0x5cf9_a199_2f0d_7c78, - 0x4f83_c10b_9eb3_330d, - 0xf6a6_3f6f_07f6_0961, - 0x0c53_b5b9_7e63_4df3, - ]), - infinity: Choice::from(0u8), - }; - assert!(!bool::from(a.is_torsion_free())); - - assert!(bool::from(G1Affine::identity().is_torsion_free())); - assert!(bool::from(G1Affine::generator().is_torsion_free())); -} - -#[test] -fn test_mul_by_x() { - // multiplying by `x` a point in G1 is the same as multiplying by - // the equivalent scalar. - let generator = G1Projective::generator(); - let x = if crate::BLS_X_IS_NEGATIVE { - -Scalar::from(crate::BLS_X) - } else { - Scalar::from(crate::BLS_X) - }; - assert_eq!(generator.mul_by_x(), generator * x); - - let point = G1Projective::generator() * Scalar::from(42); - assert_eq!(point.mul_by_x(), point * x); -} - -#[test] -fn test_clear_cofactor() { - // the generator (and the identity) are always on the curve, - // even after clearing the cofactor - let generator = G1Projective::generator(); - assert!(bool::from(generator.clear_cofactor().is_on_curve())); - let id = G1Projective::identity(); - assert!(bool::from(id.clear_cofactor().is_on_curve())); - - let point = G1Projective { - x: Fp::from_raw_unchecked([ - 0x48af5ff540c817f0, - 0xd73893acaf379d5a, - 0xe6c43584e18e023c, - 0x1eda39c30f188b3e, - 0xf618c6d3ccc0f8d8, - 0x0073542cd671e16c, - ]), - y: Fp::from_raw_unchecked([ - 0x57bf8be79461d0ba, - 0xfc61459cee3547c3, - 0x0d23567df1ef147b, - 0x0ee187bcce1d9b64, - 0xb0c8cfbe9dc8fdc1, - 0x1328661767ef368b, - ]), - z: Fp::from_raw_unchecked([ - 0x3d2d1c670671394e, - 0x0ee3a800a2f7c1ca, - 0x270f4f21da2e5050, - 0xe02840a53f1be768, - 0x55debeb597512690, - 0x08bd25353dc8f791, - ]), - }; - - assert!(bool::from(point.is_on_curve())); - assert!(!bool::from(G1Affine::from(point).is_torsion_free())); - let cleared_point = point.clear_cofactor(); - assert!(bool::from(cleared_point.is_on_curve())); - assert!(bool::from(G1Affine::from(cleared_point).is_torsion_free())); - - // in BLS12-381 the cofactor in G1 can be - // cleared multiplying by (1-x) - let h_eff = Scalar::from(1) + Scalar::from(crate::BLS_X); - assert_eq!(point.clear_cofactor(), point * h_eff); -} - -#[test] -fn test_batch_normalize() { - let a = G1Projective::generator().double(); - let b = a.double(); - let c = b.double(); - - for a_identity in (0..1).map(|n| n == 1) { - for b_identity in (0..1).map(|n| n == 1) { - for c_identity in (0..1).map(|n| n == 1) { - let mut v = [a, b, c]; - if a_identity { - v[0] = G1Projective::identity() - } - if b_identity { - v[1] = G1Projective::identity() - } - if c_identity { - v[2] = G1Projective::identity() - } - - let mut t = [ - G1Affine::identity(), - G1Affine::identity(), - G1Affine::identity(), - ]; - let expected = [ - G1Affine::from(v[0]), - G1Affine::from(v[1]), - G1Affine::from(v[2]), - ]; - - G1Projective::batch_normalize(&v[..], &mut t[..]); - - assert_eq!(&t[..], &expected[..]); - } - } - } -} diff --git a/bls12_381/src/g2.rs b/bls12_381/src/g2.rs deleted file mode 100644 index b0e7a8763..000000000 --- a/bls12_381/src/g2.rs +++ /dev/null @@ -1,2142 +0,0 @@ -//! This module provides an implementation of the $\mathbb{G}_2$ group of BLS12-381. - -use core::borrow::Borrow; -use core::fmt; -use core::iter::Sum; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use group::{ - prime::{PrimeCurve, PrimeCurveAffine, PrimeGroup}, - Curve, Group, GroupEncoding, UncompressedEncoding, WnafGroup, -}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::fp::Fp; -use crate::fp2::Fp2; -use crate::Scalar; - -/// This is an element of $\mathbb{G}_2$ represented in the affine coordinate space. -/// It is ideal to keep elements in this representation to reduce memory usage and -/// improve performance through the use of mixed curve model arithmetic. -/// -/// Values of `G2Affine` are guaranteed to be in the $q$-order subgroup unless an -/// "unchecked" API was misused. -#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] -#[derive(Copy, Clone, Debug)] -pub struct G2Affine { - pub(crate) x: Fp2, - pub(crate) y: Fp2, - infinity: Choice, -} - -impl Default for G2Affine { - fn default() -> G2Affine { - G2Affine::identity() - } -} - -impl fmt::Display for G2Affine { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl<'a> From<&'a G2Projective> for G2Affine { - fn from(p: &'a G2Projective) -> G2Affine { - let zinv = p.z.invert().unwrap_or(Fp2::zero()); - let zinv2 = zinv.square(); - let x = p.x * zinv2; - let zinv3 = zinv2 * zinv; - let y = p.y * zinv3; - - let tmp = G2Affine { - x, - y, - infinity: Choice::from(0u8), - }; - - G2Affine::conditional_select(&tmp, &G2Affine::identity(), zinv.is_zero()) - } -} - -impl From for G2Affine { - fn from(p: G2Projective) -> G2Affine { - G2Affine::from(&p) - } -} - -impl ConstantTimeEq for G2Affine { - fn ct_eq(&self, other: &Self) -> Choice { - // The only cases in which two points are equal are - // 1. infinity is set on both - // 2. infinity is not set on both, and their coordinates are equal - - (self.infinity & other.infinity) - | ((!self.infinity) - & (!other.infinity) - & self.x.ct_eq(&other.x) - & self.y.ct_eq(&other.y)) - } -} - -impl ConditionallySelectable for G2Affine { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G2Affine { - x: Fp2::conditional_select(&a.x, &b.x, choice), - y: Fp2::conditional_select(&a.y, &b.y, choice), - infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), - } - } -} - -impl Eq for G2Affine {} -impl PartialEq for G2Affine { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G2Affine { - type Output = G2Affine; - - #[inline] - fn neg(self) -> G2Affine { - G2Affine { - x: self.x, - y: Fp2::conditional_select(&-self.y, &Fp2::one(), self.infinity), - infinity: self.infinity, - } - } -} - -impl Neg for G2Affine { - type Output = G2Affine; - - #[inline] - fn neg(self) -> G2Affine { - -&self - } -} - -impl<'a, 'b> Add<&'b G2Projective> for &'a G2Affine { - type Output = G2Projective; - - #[inline] - fn add(self, rhs: &'b G2Projective) -> G2Projective { - rhs.add_mixed(self) - } -} - -impl<'a, 'b> Add<&'b G2Affine> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn add(self, rhs: &'b G2Affine) -> G2Projective { - self.add_mixed(rhs) - } -} - -impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Affine { - type Output = G2Projective; - - #[inline] - fn sub(self, rhs: &'b G2Projective) -> G2Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Sub<&'b G2Affine> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn sub(self, rhs: &'b G2Affine) -> G2Projective { - self + (-rhs) - } -} - -impl Sum for G2Projective -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl_binops_additive!(G2Projective, G2Affine); -impl_binops_additive_specify_output!(G2Affine, G2Projective, G2Projective); - -const B: Fp2 = Fp2 { - c0: 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, - ]), - c1: 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, - ]), -}; - -impl G2Affine { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G2Affine { - G2Affine { - x: Fp2::zero(), - y: Fp2::one(), - infinity: Choice::from(1u8), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G2Affine { - G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf5f2_8fa2_0294_0a10, - 0xb3f5_fb26_87b4_961a, - 0xa1a8_93b5_3e2a_e580, - 0x9894_999d_1a3c_aee9, - 0x6f67_b763_1863_366b, - 0x0581_9192_4350_bcd7, - ]), - c1: Fp::from_raw_unchecked([ - 0xa5a9_c075_9e23_f606, - 0xaaa0_c59d_bccd_60c3, - 0x3bb1_7e18_e286_7806, - 0x1b1a_b6cc_8541_b367, - 0xc2b6_ed0e_f215_8547, - 0x1192_2a09_7360_edf3, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x4c73_0af8_6049_4c4a, - 0x597c_fa1f_5e36_9c5a, - 0xe7e6_856c_aa0a_635a, - 0xbbef_b5e9_6e0d_495f, - 0x07d3_a975_f0ef_25a2, - 0x0083_fd8e_7e80_dae5, - ]), - c1: Fp::from_raw_unchecked([ - 0xadc0_fc92_df64_b05d, - 0x18aa_270a_2b14_61dc, - 0x86ad_ac6a_3be4_eba0, - 0x7949_5c4e_c93d_a33a, - 0xe717_5850_a43c_caed, - 0x0b2b_c2a1_63de_1bf2, - ]), - }, - infinity: Choice::from(0u8), - } - } - - /// Serializes this element into compressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_compressed(&self) -> [u8; 96] { - // Strictly speaking, self.x is zero already when self.infinity is true, but - // to guard against implementation mistakes we do not assume this. - let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); - - let mut res = [0; 96]; - - (&mut res[0..48]).copy_from_slice(&x.c1.to_bytes()[..]); - (&mut res[48..96]).copy_from_slice(&x.c0.to_bytes()[..]); - - // This point is in compressed form, so we set the most significant bit. - res[0] |= 1u8 << 7; - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - // Is the y-coordinate the lexicographically largest of the two associated with the - // x-coordinate? If so, set the third-most significant bit so long as this is not - // the point at infinity. - res[0] |= u8::conditional_select( - &0u8, - &(1u8 << 5), - (!self.infinity) & self.y.lexicographically_largest(), - ); - - res - } - - /// Serializes this element into uncompressed form. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn to_uncompressed(&self) -> [u8; 192] { - let mut res = [0; 192]; - - let x = Fp2::conditional_select(&self.x, &Fp2::zero(), self.infinity); - let y = Fp2::conditional_select(&self.y, &Fp2::zero(), self.infinity); - - res[0..48].copy_from_slice(&x.c1.to_bytes()[..]); - res[48..96].copy_from_slice(&x.c0.to_bytes()[..]); - res[96..144].copy_from_slice(&y.c1.to_bytes()[..]); - res[144..192].copy_from_slice(&y.c0.to_bytes()[..]); - - // Is this point at infinity? If so, set the second-most significant bit. - res[0] |= u8::conditional_select(&0u8, &(1u8 << 6), self.infinity); - - res - } - - /// Attempts to deserialize an uncompressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_uncompressed(bytes: &[u8; 192]) -> CtOption { - Self::from_uncompressed_unchecked(bytes) - .and_then(|p| CtOption::new(p, p.is_on_curve() & p.is_torsion_free())) - } - - /// Attempts to deserialize an uncompressed element, not checking if the - /// element is on the curve and not checking if it 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 `from_uncompressed()` instead. - pub fn from_uncompressed_unchecked(bytes: &[u8; 192]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let xc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - let xc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[48..96]); - - Fp::from_bytes(&tmp) - }; - - // Attempt to obtain the y-coordinate - let yc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[96..144]); - - Fp::from_bytes(&tmp) - }; - let yc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[144..192]); - - Fp::from_bytes(&tmp) - }; - - xc1.and_then(|xc1| { - xc0.and_then(|xc0| { - yc1.and_then(|yc1| { - yc0.and_then(|yc0| { - let x = Fp2 { - c0: xc0, - c1: xc1 - }; - let y = Fp2 { - c0: yc0, - c1: yc1 - }; - - // Create a point representing this value - let p = G2Affine::conditional_select( - &G2Affine { - x, - y, - infinity: infinity_flag_set, - }, - &G2Affine::identity(), - infinity_flag_set, - ); - - CtOption::new( - p, - // If the infinity flag is set, the x and y coordinates should have been zero. - ((!infinity_flag_set) | (infinity_flag_set & x.is_zero() & y.is_zero())) & - // The compression flag should not have been set, as this is an uncompressed element - (!compression_flag_set) & - // The sort flag should not have been set, as this is an uncompressed element - (!sort_flag_set), - ) - }) - }) - }) - }) - } - - /// Attempts to deserialize a compressed element. See [`notes::serialization`](crate::notes::serialization) - /// for details about how group elements are serialized. - pub fn from_compressed(bytes: &[u8; 96]) -> CtOption { - // We already know the point is on the curve because this is established - // by the y-coordinate recovery procedure in from_compressed_unchecked(). - - Self::from_compressed_unchecked(bytes).and_then(|p| CtOption::new(p, p.is_torsion_free())) - } - - /// 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 `from_compressed()` instead. - pub fn from_compressed_unchecked(bytes: &[u8; 96]) -> CtOption { - // Obtain the three flags from the start of the byte sequence - let compression_flag_set = Choice::from((bytes[0] >> 7) & 1); - let infinity_flag_set = Choice::from((bytes[0] >> 6) & 1); - let sort_flag_set = Choice::from((bytes[0] >> 5) & 1); - - // Attempt to obtain the x-coordinate - let xc1 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[0..48]); - - // Mask away the flag bits - tmp[0] &= 0b0001_1111; - - Fp::from_bytes(&tmp) - }; - let xc0 = { - let mut tmp = [0; 48]; - tmp.copy_from_slice(&bytes[48..96]); - - Fp::from_bytes(&tmp) - }; - - xc1.and_then(|xc1| { - xc0.and_then(|xc0| { - let x = Fp2 { c0: xc0, c1: xc1 }; - - // If the infinity flag is set, return the value assuming - // the x-coordinate is zero and the sort bit is not set. - // - // Otherwise, return a recovered point (assuming the correct - // y-coordinate can be found) so long as the infinity flag - // was not set. - CtOption::new( - G2Affine::identity(), - infinity_flag_set & // Infinity flag should be set - compression_flag_set & // Compression flag should be set - (!sort_flag_set) & // Sort flag should not be set - x.is_zero(), // The x-coordinate should be zero - ) - .or_else(|| { - // Recover a y-coordinate given x by y = sqrt(x^3 + 4) - ((x.square() * x) + B).sqrt().and_then(|y| { - // Switch to the correct y-coordinate if necessary. - let y = Fp2::conditional_select( - &y, - &-y, - y.lexicographically_largest() ^ sort_flag_set, - ); - - CtOption::new( - G2Affine { - x, - y, - infinity: infinity_flag_set, - }, - (!infinity_flag_set) & // Infinity flag should not be set - compression_flag_set, // Compression flag should be set - ) - }) - }) - }) - }) - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.infinity - } - - /// Returns true if this point is free of an $h$-torsion component, and so it - /// exists within the $q$-order subgroup $\mathbb{G}_2$. This should always return true - /// unless an "unchecked" API was used. - pub fn is_torsion_free(&self) -> Choice { - const FQ_MODULUS_BYTES: [u8; 32] = [ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, - ]; - - // Clear the r-torsion from the point and check if it is the identity - G2Projective::from(*self) - .multiply(&FQ_MODULUS_BYTES) - .is_identity() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // y^2 - x^3 ?= 4(u + 1) - (self.y.square() - (self.x.square() * self.x)).ct_eq(&B) | self.infinity - } -} - -/// This is an element of $\mathbb{G}_2$ represented in the projective coordinate space. -#[cfg_attr(docsrs, doc(cfg(feature = "groups")))] -#[derive(Copy, Clone, Debug)] -pub struct G2Projective { - pub(crate) x: Fp2, - pub(crate) y: Fp2, - pub(crate) z: Fp2, -} - -impl Default for G2Projective { - fn default() -> G2Projective { - G2Projective::identity() - } -} - -impl fmt::Display for G2Projective { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl<'a> From<&'a G2Affine> for G2Projective { - fn from(p: &'a G2Affine) -> G2Projective { - G2Projective { - x: p.x, - y: p.y, - z: Fp2::conditional_select(&Fp2::one(), &Fp2::zero(), p.infinity), - } - } -} - -impl From for G2Projective { - fn from(p: G2Affine) -> G2Projective { - G2Projective::from(&p) - } -} - -impl ConstantTimeEq for G2Projective { - fn ct_eq(&self, other: &Self) -> Choice { - // Is (xz^2, yz^3, z) equal to (x'z'^2, yz'^3, z') when converted to affine? - - let z = other.z.square(); - let x1 = self.x * z; - let z = z * other.z; - let y1 = self.y * z; - let z = self.z.square(); - let x2 = other.x * z; - let z = z * self.z; - let y2 = other.y * z; - - let self_is_zero = self.z.is_zero(); - let other_is_zero = other.z.is_zero(); - - (self_is_zero & other_is_zero) // Both point at infinity - | ((!self_is_zero) & (!other_is_zero) & x1.ct_eq(&x2) & y1.ct_eq(&y2)) - // Neither point at infinity, coordinates are the same - } -} - -impl ConditionallySelectable for G2Projective { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - G2Projective { - x: Fp2::conditional_select(&a.x, &b.x, choice), - y: Fp2::conditional_select(&a.y, &b.y, choice), - z: Fp2::conditional_select(&a.z, &b.z, choice), - } - } -} - -impl Eq for G2Projective {} -impl PartialEq for G2Projective { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl<'a> Neg for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn neg(self) -> G2Projective { - G2Projective { - x: self.x, - y: -self.y, - z: self.z, - } - } -} - -impl Neg for G2Projective { - type Output = G2Projective; - - #[inline] - fn neg(self) -> G2Projective { - -&self - } -} - -impl<'a, 'b> Add<&'b G2Projective> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn add(self, rhs: &'b G2Projective) -> G2Projective { - self.add(rhs) - } -} - -impl<'a, 'b> Sub<&'b G2Projective> for &'a G2Projective { - type Output = G2Projective; - - #[inline] - fn sub(self, rhs: &'b G2Projective) -> G2Projective { - self + (-rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G2Projective { - type Output = G2Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - self.multiply(&other.to_bytes()) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a G2Affine { - type Output = G2Projective; - - fn mul(self, other: &'b Scalar) -> Self::Output { - G2Projective::from(self).multiply(&other.to_bytes()) - } -} - -impl_binops_additive!(G2Projective, G2Projective); -impl_binops_multiplicative!(G2Projective, Scalar); -impl_binops_multiplicative_mixed!(G2Affine, Scalar, G2Projective); - -impl G2Projective { - /// Returns the identity of the group: the point at infinity. - pub fn identity() -> G2Projective { - G2Projective { - x: Fp2::zero(), - y: Fp2::one(), - z: Fp2::zero(), - } - } - - /// Returns a fixed generator of the group. See [`notes::design`](notes/design/index.html#fixed-generators) - /// for how this generator is chosen. - pub fn generator() -> G2Projective { - G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xf5f2_8fa2_0294_0a10, - 0xb3f5_fb26_87b4_961a, - 0xa1a8_93b5_3e2a_e580, - 0x9894_999d_1a3c_aee9, - 0x6f67_b763_1863_366b, - 0x0581_9192_4350_bcd7, - ]), - c1: Fp::from_raw_unchecked([ - 0xa5a9_c075_9e23_f606, - 0xaaa0_c59d_bccd_60c3, - 0x3bb1_7e18_e286_7806, - 0x1b1a_b6cc_8541_b367, - 0xc2b6_ed0e_f215_8547, - 0x1192_2a09_7360_edf3, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x4c73_0af8_6049_4c4a, - 0x597c_fa1f_5e36_9c5a, - 0xe7e6_856c_aa0a_635a, - 0xbbef_b5e9_6e0d_495f, - 0x07d3_a975_f0ef_25a2, - 0x0083_fd8e_7e80_dae5, - ]), - c1: Fp::from_raw_unchecked([ - 0xadc0_fc92_df64_b05d, - 0x18aa_270a_2b14_61dc, - 0x86ad_ac6a_3be4_eba0, - 0x7949_5c4e_c93d_a33a, - 0xe717_5850_a43c_caed, - 0x0b2b_c2a1_63de_1bf2, - ]), - }, - z: Fp2::one(), - } - } - - /// Computes the doubling of this point. - pub fn double(&self) -> G2Projective { - // http://www.hyperelliptic.org/EFD/g2p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l - // - // There are no points of order 2. - - let a = self.x.square(); - let b = self.y.square(); - let c = b.square(); - let d = self.x + b; - let d = d.square(); - let d = d - a - c; - let d = d + d; - let e = a + a + a; - let f = e.square(); - let z3 = self.z * self.y; - let z3 = z3 + z3; - let x3 = f - (d + d); - let c = c + c; - let c = c + c; - let c = c + c; - let y3 = e * (d - x3) - c; - - let tmp = G2Projective { - x: x3, - y: y3, - z: z3, - }; - - G2Projective::conditional_select(&tmp, &G2Projective::identity(), self.is_identity()) - } - - /// Adds this point to another point. - pub fn add(&self, rhs: &G2Projective) -> G2Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G2Projective::conditional_select(self, rhs, f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let z = rhs.z.square(); - let u1 = self.x * z; - let z = z * rhs.z; - let s1 = self.y * z; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp2::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z * rhs.z; // We allow rhs.z != 1, so we must account for this. - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G2Projective { - x: x3, - y: y3, - z: z3, - }; - - G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - /// Adds this point to another point in the affine model. - pub fn add_mixed(&self, rhs: &G2Affine) -> G2Projective { - // This Jacobian point addition technique is based on the implementation in libsecp256k1, - // which assumes that rhs has z=1. Let's address the case of zero z-coordinates generally. - - // If self is the identity, return rhs. Otherwise, return self. The other cases will be - // predicated on neither self nor rhs being the identity. - let f1 = self.is_identity(); - let res = G2Projective::conditional_select(self, &G2Projective::from(rhs), f1); - let f2 = rhs.is_identity(); - - // If neither are the identity but x1 = x2 and y1 != y2, then return the identity - let u1 = self.x; - let s1 = self.y; - let z = self.z.square(); - let u2 = rhs.x * z; - let z = z * self.z; - let s2 = rhs.y * z; - let f3 = u1.ct_eq(&u2) & (!s1.ct_eq(&s2)); - let res = - G2Projective::conditional_select(&res, &G2Projective::identity(), (!f1) & (!f2) & f3); - - let t = u1 + u2; - let m = s1 + s2; - let rr = t.square(); - let m_alt = -u2; - let tt = u1 * m_alt; - let rr = rr + tt; - - // Correct for x1 != x2 but y1 = -y2, which can occur because p - 1 is divisible by 3. - // libsecp256k1 does this by substituting in an alternative (defined) expression for lambda. - let degenerate = m.is_zero() & rr.is_zero(); - let rr_alt = s1 + s1; - let m_alt = m_alt + u1; - let rr_alt = Fp2::conditional_select(&rr_alt, &rr, !degenerate); - let m_alt = Fp2::conditional_select(&m_alt, &m, !degenerate); - - let n = m_alt.square(); - let q = n * t; - - let n = n.square(); - let n = Fp2::conditional_select(&n, &m, degenerate); - let t = rr_alt.square(); - let z3 = m_alt * self.z; - let z3 = z3 + z3; - let q = -q; - let t = t + q; - let x3 = t; - let t = t + t; - let t = t + q; - let t = t * rr_alt; - let t = t + n; - let y3 = -t; - let x3 = x3 + x3; - let x3 = x3 + x3; - let y3 = y3 + y3; - let y3 = y3 + y3; - - let tmp = G2Projective { - x: x3, - y: y3, - z: z3, - }; - - G2Projective::conditional_select(&res, &tmp, (!f1) & (!f2) & (!f3)) - } - - fn multiply(&self, by: &[u8]) -> G2Projective { - let mut acc = G2Projective::identity(); - - // This is a simple double-and-add implementation of point - // 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 by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(1) - { - acc = acc.double(); - acc = G2Projective::conditional_select(&acc, &(acc + self), bit); - } - - acc - } - - #[cfg(feature = "endo")] - fn psi(&self) -> G2Projective { - // 1 / ((u+1) ^ ((q-1)/3)) - let psi_coeff_x = Fp2 { - c0: Fp::zero(), - c1: Fp::from_raw_unchecked([ - 0x890dc9e4867545c3, - 0x2af322533285a5d5, - 0x50880866309b7e2c, - 0xa20d1b8c7e881024, - 0x14e4f04fe2db9068, - 0x14e56d3f1564853a, - ]), - }; - // 1 / ((u+1) ^ (p-1)/2) - let psi_coeff_y = Fp2 { - c0: Fp::from_raw_unchecked([ - 0x3e2f585da55c9ad1, - 0x4294213d86c18183, - 0x382844c88b623732, - 0x92ad2afd19103e18, - 0x1d794e4fac7cf0b9, - 0x0bd592fc7d825ec8, - ]), - c1: Fp::from_raw_unchecked([ - 0x7bcfa7a25aa30fda, - 0xdc17dec12a927e7c, - 0x2f088dd86b4ebef1, - 0xd1ca2087da74d4a7, - 0x2da2596696cebc1d, - 0x0e2b7eedbbfd87d2, - ]), - }; - - G2Projective { - // x = frobenius(x)/((u+1)^((p-1)/3)) - x: self.x.frobenius_map() * psi_coeff_x, - // y = frobenius(y)/(u+1)^((p-1)/2) - y: self.y.frobenius_map() * psi_coeff_y, - // z = frobenius(z) - z: self.z.frobenius_map(), - } - } - - #[cfg(feature = "endo")] - fn psi2(&self) -> G2Projective { - // 1 / 2 ^ ((q-1)/3) - let psi2_coeff_x = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xcd03c9e48671f071, - 0x5dab22461fcda5d2, - 0x587042afd3851b95, - 0x8eb60ebe01bacb9e, - 0x03f97d6e83d050d2, - 0x18f0206554638741, - ]), - c1: Fp::zero(), - }; - - G2Projective { - // x = frobenius^2(x)/2^((p-1)/3) - x: self.x.frobenius_map().frobenius_map() * psi2_coeff_x, - // y = -frobenius^2(y) - y: self.y.frobenius_map().frobenius_map().neg(), - // z = z - z: self.z, - } - } - - /// Multiply `self` by `crate::BLS_X`, using double and add. - #[cfg(feature = "endo")] - fn mul_by_x(&self) -> G2Projective { - let mut xself = G2Projective::identity(); - // NOTE: in BLS12-381 we can just skip the first bit. - let mut x = crate::BLS_X >> 1; - let mut acc = *self; - while x != 0 { - acc = acc.double(); - if x % 2 == 1 { - xself += acc; - } - x >>= 1; - } - // finally, flip the sign - if crate::BLS_X_IS_NEGATIVE { - xself = -xself; - } - xself - } - - /// Clears the cofactor, using [Budroni-Pintore](https://ia.cr/2017/419). - /// This is equivalent to multiplying by $h\_\textrm{eff} = 3(z^2 - 1) \cdot - /// h_2$, where $h_2$ is the cofactor of $\mathbb{G}\_2$ and $z$ is the - /// parameter of BLS12-381. - /// - /// The endomorphism is only actually used if the crate feature `endo` is - /// enabled, and it is disabled by default to mitigate potential patent - /// issues. - pub fn clear_cofactor(&self) -> G2Projective { - #[cfg(feature = "endo")] - fn clear_cofactor(this: &G2Projective) -> G2Projective { - let t1 = this.mul_by_x(); // [x] P - let t2 = this.psi(); // psi(P) - - this.double().psi2() // psi^2(2P) - + (t1 + t2).mul_by_x() // psi^2(2P) + [x^2] P + [x] psi(P) - - t1 // psi^2(2P) + [x^2 - x] P + [x] psi(P) - - t2 // psi^2(2P) + [x^2 - x] P + [x - 1] psi(P) - - this // psi^2(2P) + [x^2 - x - 1] P + [x - 1] psi(P) - } - - #[cfg(not(feature = "endo"))] - fn clear_cofactor(this: &G2Projective) -> G2Projective { - this.multiply(&[ - 0x51, 0x55, 0xa9, 0xaa, 0x5, 0x0, 0x2, 0xe8, 0xb4, 0xf6, 0xbb, 0xde, 0xa, 0x4c, - 0x89, 0x59, 0xa3, 0xf6, 0x89, 0x66, 0xc0, 0xcb, 0x54, 0xe9, 0x1a, 0x7c, 0x47, 0xd7, - 0x69, 0xec, 0xc0, 0x2e, 0xb0, 0x12, 0x12, 0x5d, 0x1, 0xbf, 0x82, 0x6d, 0x95, 0xdb, - 0x31, 0x87, 0x17, 0x2f, 0x9c, 0x32, 0xe1, 0xff, 0x8, 0x15, 0x3, 0xff, 0x86, 0x99, - 0x68, 0xd7, 0x5a, 0x14, 0xe9, 0xa8, 0xe2, 0x88, 0x28, 0x35, 0x1b, 0xa9, 0xe, 0x6a, - 0x4c, 0x58, 0xb3, 0x75, 0xee, 0xf2, 0x8, 0x9f, 0xc6, 0xb, - ]) - } - - clear_cofactor(self) - } - - /// Converts a batch of `G2Projective` elements into `G2Affine` elements. This - /// function will panic if `p.len() != q.len()`. - pub fn batch_normalize(p: &[Self], q: &mut [G2Affine]) { - assert_eq!(p.len(), q.len()); - - let mut acc = Fp2::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `x` field of `G2Affine` to store the product - // of previous z-coordinates seen. - q.x = acc; - - // We will end up skipping all identities in p - acc = Fp2::conditional_select(&(acc * p.z), &acc, p.is_identity()); - } - - // This is the inverse, as all z-coordinates are nonzero and the ones - // that are not are skipped. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().rev().zip(q.iter_mut().rev()) { - let skip = p.is_identity(); - - // Compute tmp = 1/z - let tmp = q.x * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc = Fp2::conditional_select(&(acc * p.z), &acc, skip); - - // Set the coordinates to the correct value - let tmp2 = tmp.square(); - let tmp3 = tmp2 * tmp; - - q.x = p.x * tmp2; - q.y = p.y * tmp3; - q.infinity = Choice::from(0u8); - - *q = G2Affine::conditional_select(&q, &G2Affine::identity(), skip); - } - } - - /// Returns true if this element is the identity (the point at infinity). - #[inline] - pub fn is_identity(&self) -> Choice { - self.z.is_zero() - } - - /// Returns true if this point is on the curve. This should always return - /// true unless an "unchecked" API was used. - pub fn is_on_curve(&self) -> Choice { - // Y^2 - X^3 = 4(u + 1)(Z^6) - - (self.y.square() - (self.x.square() * self.x)) - .ct_eq(&((self.z.square() * self.z).square() * B)) - | self.z.is_zero() - } -} - -pub struct G2Compressed([u8; 96]); - -impl fmt::Debug for G2Compressed { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0[..].fmt(f) - } -} - -impl Default for G2Compressed { - fn default() -> Self { - G2Compressed([0; 96]) - } -} - -impl AsRef<[u8]> for G2Compressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for G2Compressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -pub struct G2Uncompressed([u8; 192]); - -impl fmt::Debug for G2Uncompressed { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.0[..].fmt(f) - } -} - -impl Default for G2Uncompressed { - fn default() -> Self { - G2Uncompressed([0; 192]) - } -} - -impl AsRef<[u8]> for G2Uncompressed { - fn as_ref(&self) -> &[u8] { - &self.0 - } -} - -impl AsMut<[u8]> for G2Uncompressed { - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } -} - -impl Group for G2Projective { - type Scalar = Scalar; - - fn random(rng: &mut R) -> Self { - loop { - let x = Fp2::random(rng); - let flip_sign = rng.next_u32() % 2 != 0; - - // Obtain the corresponding y-coordinate given x as y = sqrt(x^3 + 4) - let p = ((x.square() * x) + B).sqrt().map(|y| G2Affine { - x, - y: if flip_sign { -y } else { y }, - infinity: 0.into(), - }); - - if p.is_some().into() { - let p = p.unwrap().to_curve().clear_cofactor(); - - if bool::from(!p.is_identity()) { - return p; - } - } - } - } - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - Self::generator() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } -} - -impl WnafGroup for G2Projective { - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - const RECOMMENDATIONS: [usize; 11] = [1, 3, 8, 20, 47, 126, 260, 826, 1501, 4555, 84071]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } -} - -impl PrimeGroup for G2Projective {} - -impl Curve for G2Projective { - type AffineRepr = G2Affine; - - fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { - Self::batch_normalize(p, q); - } - - fn to_affine(&self) -> Self::AffineRepr { - self.into() - } -} - -impl PrimeCurve for G2Projective { - type Affine = G2Affine; -} - -impl PrimeCurveAffine for G2Affine { - type Scalar = Scalar; - type Curve = G2Projective; - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - Self::generator() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - fn to_curve(&self) -> Self::Curve { - self.into() - } -} - -impl GroupEncoding for G2Projective { - type Repr = G2Compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - G2Affine::from_bytes(bytes).map(Self::from) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - G2Affine::from_bytes_unchecked(bytes).map(Self::from) - } - - fn to_bytes(&self) -> Self::Repr { - G2Affine::from(self).to_bytes() - } -} - -impl GroupEncoding for G2Affine { - type Repr = G2Compressed; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - Self::from_compressed(&bytes.0) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - Self::from_compressed_unchecked(&bytes.0) - } - - fn to_bytes(&self) -> Self::Repr { - G2Compressed(self.to_compressed()) - } -} - -impl UncompressedEncoding for G2Affine { - type Uncompressed = G2Uncompressed; - - fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption { - Self::from_uncompressed(&bytes.0) - } - - fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption { - Self::from_uncompressed_unchecked(&bytes.0) - } - - fn to_uncompressed(&self) -> Self::Uncompressed { - G2Uncompressed(self.to_uncompressed()) - } -} - -#[test] -fn test_is_on_curve() { - assert!(bool::from(G2Affine::identity().is_on_curve())); - assert!(bool::from(G2Affine::generator().is_on_curve())); - assert!(bool::from(G2Projective::identity().is_on_curve())); - assert!(bool::from(G2Projective::generator().is_on_curve())); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - let gen = G2Affine::generator(); - let mut test = G2Projective { - x: gen.x * (z.square()), - y: gen.y * (z.square() * z), - z, - }; - - assert!(bool::from(test.is_on_curve())); - - test.x = z; - assert!(!bool::from(test.is_on_curve())); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_affine_point_equality() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_point_equality() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); - - assert!(a == a); - assert!(b == b); - assert!(a != b); - assert!(b != a); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - let mut c = G2Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - assert!(bool::from(c.is_on_curve())); - - assert!(a == c); - assert!(b != c); - assert!(c == a); - assert!(c != b); - - c.y = -c.y; - assert!(bool::from(c.is_on_curve())); - - assert!(a != c); - assert!(b != c); - assert!(c != a); - assert!(c != b); - - c.y = -c.y; - c.x = z; - assert!(!bool::from(c.is_on_curve())); - assert!(a != b); - assert!(a != c); - assert!(b != c); -} - -#[test] -fn test_conditionally_select_affine() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(0u8)), a); - assert_eq!(G2Affine::conditional_select(&a, &b, Choice::from(1u8)), b); -} - -#[test] -fn test_conditionally_select_projective() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); - - assert_eq!( - G2Projective::conditional_select(&a, &b, Choice::from(0u8)), - a - ); - assert_eq!( - G2Projective::conditional_select(&a, &b, Choice::from(1u8)), - b - ); -} - -#[test] -fn test_projective_to_affine() { - let a = G2Projective::generator(); - let b = G2Projective::identity(); - - assert!(bool::from(G2Affine::from(a).is_on_curve())); - assert!(!bool::from(G2Affine::from(a).is_identity())); - assert!(bool::from(G2Affine::from(b).is_on_curve())); - assert!(bool::from(G2Affine::from(b).is_identity())); - - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - let c = G2Projective { - x: a.x * (z.square()), - y: a.y * (z.square() * z), - z, - }; - - assert_eq!(G2Affine::from(c), G2Affine::generator()); -} - -#[test] -fn test_affine_to_projective() { - let a = G2Affine::generator(); - let b = G2Affine::identity(); - - assert!(bool::from(G2Projective::from(a).is_on_curve())); - assert!(!bool::from(G2Projective::from(a).is_identity())); - assert!(bool::from(G2Projective::from(b).is_on_curve())); - assert!(bool::from(G2Projective::from(b).is_identity())); -} - -#[test] -fn test_doubling() { - { - let tmp = G2Projective::identity().double(); - assert!(bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - } - { - let tmp = G2Projective::generator().double(); - assert!(!bool::from(tmp.is_identity())); - assert!(bool::from(tmp.is_on_curve())); - - assert_eq!( - G2Affine::from(tmp), - G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xe9d9_e2da_9620_f98b, - 0x54f1_1993_46b9_7f36, - 0x3db3_b820_376b_ed27, - 0xcfdb_31c9_b0b6_4f4c, - 0x41d7_c127_8635_4493, - 0x0571_0794_c255_c064, - ]), - c1: Fp::from_raw_unchecked([ - 0xd6c1_d3ca_6ea0_d06e, - 0xda0c_bd90_5595_489f, - 0x4f53_52d4_3479_221d, - 0x8ade_5d73_6f8c_97e0, - 0x48cc_8433_925e_f70e, - 0x08d7_ea71_ea91_ef81, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x15ba_26eb_4b0d_186f, - 0x0d08_6d64_b7e9_e01e, - 0xc8b8_48dd_652f_4c78, - 0xeecf_46a6_123b_ae4f, - 0x255e_8dd8_b6dc_812a, - 0x1641_42af_21dc_f93f, - ]), - c1: Fp::from_raw_unchecked([ - 0xf9b4_a1a8_9598_4db4, - 0xd417_b114_cccf_f748, - 0x6856_301f_c89f_086e, - 0x41c7_7787_8931_e3da, - 0x3556_b155_066a_2105, - 0x00ac_f7d3_25cb_89cf, - ]), - }, - infinity: Choice::from(0u8) - } - ); - } -} - -#[test] -fn test_projective_addition() { - { - let a = G2Projective::identity(); - let b = G2Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G2Projective::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::generator().double().double(); // 4P - let b = G2Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G2Projective::generator(); - for _ in 0..5 { - d += G2Projective::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp2 { - c0: 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, - ]), - c1: Fp::zero(), - }; - let beta = beta.square(); - let a = G2Projective::generator().double().double(); - let b = G2Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G2Affine::from(c), - G2Affine::from(G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x705a_bc79_9ca7_73d3, - 0xfe13_2292_c1d4_bf08, - 0xf37e_ce3e_07b2_b466, - 0x887e_1c43_f447_e301, - 0x1e09_70d0_33bc_77e8, - 0x1985_c81e_20a6_93f2, - ]), - c1: Fp::from_raw_unchecked([ - 0x1d79_b25d_b36a_b924, - 0x2394_8e4d_5296_39d3, - 0x471b_a7fb_0d00_6297, - 0x2c36_d4b4_465d_c4c0, - 0x82bb_c3cf_ec67_f538, - 0x051d_2728_b67b_f952, - ]) - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x41b1_bbf6_576c_0abf, - 0xb6cc_9371_3f7a_0f9a, - 0x6b65_b43e_48f3_f01f, - 0xfb7a_4cfc_af81_be4f, - 0x3e32_dadc_6ec2_2cb6, - 0x0bb0_fc49_d798_07e3, - ]), - c1: Fp::from_raw_unchecked([ - 0x7d13_9778_8f5f_2ddf, - 0xab29_0714_4ff0_d8e8, - 0x5b75_73e0_cdb9_1f92, - 0x4cb8_932d_d31d_af28, - 0x62bb_fac6_db05_2a54, - 0x11f9_5c16_d14c_3bbe, - ]) - }, - z: Fp2::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -fn test_mixed_addition() { - { - let a = G2Affine::identity(); - let b = G2Projective::identity(); - let c = a + b; - assert!(bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } - { - let a = G2Affine::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = a + b; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Affine::identity(); - let mut b = G2Projective::generator(); - { - let z = Fp2 { - c0: Fp::from_raw_unchecked([ - 0xba7a_fa1f_9a6f_e250, - 0xfa0f_5b59_5eaf_e731, - 0x3bdc_4776_94c3_06e7, - 0x2149_be4b_3949_fa24, - 0x64aa_6e06_49b2_078c, - 0x12b1_08ac_3364_3c3e, - ]), - c1: Fp::from_raw_unchecked([ - 0x1253_25df_3d35_b5a8, - 0xdc46_9ef5_555d_7fe3, - 0x02d7_16d2_4431_06a9, - 0x05a1_db59_a6ff_37d0, - 0x7cf7_784e_5300_bb8f, - 0x16a8_8922_c7a5_e844, - ]), - }; - - b = G2Projective { - x: b.x * (z.square()), - y: b.y * (z.square() * z), - z, - }; - } - let c = b + a; - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(c == G2Projective::generator()); - } - { - let a = G2Projective::generator().double().double(); // 4P - let b = G2Projective::generator().double(); // 2P - let c = a + b; - - let mut d = G2Projective::generator(); - for _ in 0..5 { - d += G2Affine::generator(); - } - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - assert!(!bool::from(d.is_identity())); - assert!(bool::from(d.is_on_curve())); - assert_eq!(c, d); - } - - // Degenerate case - { - let beta = Fp2 { - c0: 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, - ]), - c1: Fp::zero(), - }; - let beta = beta.square(); - let a = G2Projective::generator().double().double(); - let b = G2Projective { - x: a.x * beta, - y: -a.y, - z: a.z, - }; - let a = G2Affine::from(a); - assert!(bool::from(a.is_on_curve())); - assert!(bool::from(b.is_on_curve())); - - let c = a + b; - assert_eq!( - G2Affine::from(c), - G2Affine::from(G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x705a_bc79_9ca7_73d3, - 0xfe13_2292_c1d4_bf08, - 0xf37e_ce3e_07b2_b466, - 0x887e_1c43_f447_e301, - 0x1e09_70d0_33bc_77e8, - 0x1985_c81e_20a6_93f2, - ]), - c1: Fp::from_raw_unchecked([ - 0x1d79_b25d_b36a_b924, - 0x2394_8e4d_5296_39d3, - 0x471b_a7fb_0d00_6297, - 0x2c36_d4b4_465d_c4c0, - 0x82bb_c3cf_ec67_f538, - 0x051d_2728_b67b_f952, - ]) - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x41b1_bbf6_576c_0abf, - 0xb6cc_9371_3f7a_0f9a, - 0x6b65_b43e_48f3_f01f, - 0xfb7a_4cfc_af81_be4f, - 0x3e32_dadc_6ec2_2cb6, - 0x0bb0_fc49_d798_07e3, - ]), - c1: Fp::from_raw_unchecked([ - 0x7d13_9778_8f5f_2ddf, - 0xab29_0714_4ff0_d8e8, - 0x5b75_73e0_cdb9_1f92, - 0x4cb8_932d_d31d_af28, - 0x62bb_fac6_db05_2a54, - 0x11f9_5c16_d14c_3bbe, - ]) - }, - z: Fp2::one() - }) - ); - assert!(!bool::from(c.is_identity())); - assert!(bool::from(c.is_on_curve())); - } -} - -#[test] -#[allow(clippy::eq_op)] -fn test_projective_negation_and_subtraction() { - let a = G2Projective::generator().double(); - assert_eq!(a + (-a), G2Projective::identity()); - assert_eq!(a + (-a), a - a); -} - -#[test] -fn test_affine_negation_and_subtraction() { - let a = G2Affine::generator(); - assert_eq!(G2Projective::from(a) + (-a), G2Projective::identity()); - assert_eq!(G2Projective::from(a) + (-a), G2Projective::from(a) - a); -} - -#[test] -fn test_projective_scalar_multiplication() { - let g = G2Projective::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!((g * a) * b, g * c); -} - -#[test] -fn test_affine_scalar_multiplication() { - let g = G2Affine::generator(); - let a = Scalar::from_raw([ - 0x2b56_8297_a56d_a71c, - 0xd8c3_9ecb_0ef3_75d1, - 0x435c_38da_67bf_bf96, - 0x8088_a050_26b6_59b2, - ]); - let b = Scalar::from_raw([ - 0x785f_dd9b_26ef_8b85, - 0xc997_f258_3769_5c18, - 0x4c8d_bc39_e7b7_56c1, - 0x70d9_b6cc_6d87_df20, - ]); - let c = a * b; - - assert_eq!(G2Affine::from(g * a) * b, g * c); -} - -#[test] -fn test_is_torsion_free() { - let a = G2Affine { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x89f5_50c8_13db_6431, - 0xa50b_e8c4_56cd_8a1a, - 0xa45b_3741_14ca_e851, - 0xbb61_90f5_bf7f_ff63, - 0x970c_a02c_3ba8_0bc7, - 0x02b8_5d24_e840_fbac, - ]), - c1: Fp::from_raw_unchecked([ - 0x6888_bc53_d707_16dc, - 0x3dea_6b41_1768_2d70, - 0xd8f5_f930_500c_a354, - 0x6b5e_cb65_56f5_c155, - 0xc96b_ef04_3477_8ab0, - 0x0508_1505_5150_06ad, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x3cf1_ea0d_434b_0f40, - 0x1a0d_c610_e603_e333, - 0x7f89_9561_60c7_2fa0, - 0x25ee_03de_cf64_31c5, - 0xeee8_e206_ec0f_e137, - 0x0975_92b2_26df_ef28, - ]), - c1: Fp::from_raw_unchecked([ - 0x71e8_bb5f_2924_7367, - 0xa5fe_049e_2118_31ce, - 0x0ce6_b354_502a_3896, - 0x93b0_1200_0997_314e, - 0x6759_f3b6_aa5b_42ac, - 0x1569_44c4_dfe9_2bbb, - ]), - }, - infinity: Choice::from(0u8), - }; - assert!(!bool::from(a.is_torsion_free())); - - assert!(bool::from(G2Affine::identity().is_torsion_free())); - assert!(bool::from(G2Affine::generator().is_torsion_free())); -} - -#[cfg(feature = "endo")] -#[test] -fn test_mul_by_x() { - // multiplying by `x` a point in G2 is the same as multiplying by - // the equivalent scalar. - let generator = G2Projective::generator(); - let x = if crate::BLS_X_IS_NEGATIVE { - -Scalar::from(crate::BLS_X) - } else { - Scalar::from(crate::BLS_X) - }; - assert_eq!(generator.mul_by_x(), generator * x); - - let point = G2Projective::generator() * Scalar::from(42); - assert_eq!(point.mul_by_x(), point * x); -} - -#[cfg(feature = "endo")] -#[test] -fn test_psi() { - let generator = G2Projective::generator(); - - // `point` is a random point in the curve - let point = G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xee4c8cb7c047eaf2, - 0x44ca22eee036b604, - 0x33b3affb2aefe101, - 0x15d3e45bbafaeb02, - 0x7bfc2154cd7419a4, - 0x0a2d0c2b756e5edc, - ]), - c1: Fp::from_raw_unchecked([ - 0xfc224361029a8777, - 0x4cbf2baab8740924, - 0xc5008c6ec6592c89, - 0xecc2c57b472a9c2d, - 0x8613eafd9d81ffb1, - 0x10fe54daa2d3d495, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7de7edc43953b75c, - 0x58be1d2de35e87dc, - 0x5731d30b0e337b40, - 0xbe93b60cfeaae4c9, - 0x8b22c203764bedca, - 0x01616c8d1033b771, - ]), - c1: Fp::from_raw_unchecked([ - 0xea126fe476b5733b, - 0x85cee68b5dae1652, - 0x98247779f7272b04, - 0xa649c8b468c6e808, - 0xb5b9a62dff0c4e45, - 0x1555b67fc7bbe73d, - ]), - }, - z: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0ef2ddffab187c0a, - 0x2424522b7d5ecbfc, - 0xc6f341a3398054f4, - 0x5523ddf409502df0, - 0xd55c0b5a88e0dd97, - 0x066428d704923e52, - ]), - c1: Fp::from_raw_unchecked([ - 0x538bbe0c95b4878d, - 0xad04a50379522881, - 0x6d5c05bf5c12fb64, - 0x4ce4a069a2d34787, - 0x59ea6c8d0dffaeaf, - 0x0d42a083a75bd6f3, - ]), - }, - }; - assert!(bool::from(point.is_on_curve())); - - // psi2(P) = psi(psi(P)) - assert_eq!(generator.psi2(), generator.psi().psi()); - assert_eq!(point.psi2(), point.psi().psi()); - // psi(P) is a morphism - assert_eq!(generator.double().psi(), generator.psi().double()); - assert_eq!(point.psi() + generator.psi(), (point + generator).psi()); - // psi(P) behaves in the same way on the same projective point - let mut normalized_point = [G2Affine::identity()]; - G2Projective::batch_normalize(&[point], &mut normalized_point); - let normalized_point = G2Projective::from(normalized_point[0]); - assert_eq!(point.psi(), normalized_point.psi()); - assert_eq!(point.psi2(), normalized_point.psi2()); -} - -#[test] -fn test_clear_cofactor() { - // `point` is a random point in the curve - let point = G2Projective { - x: Fp2 { - c0: Fp::from_raw_unchecked([ - 0xee4c8cb7c047eaf2, - 0x44ca22eee036b604, - 0x33b3affb2aefe101, - 0x15d3e45bbafaeb02, - 0x7bfc2154cd7419a4, - 0x0a2d0c2b756e5edc, - ]), - c1: Fp::from_raw_unchecked([ - 0xfc224361029a8777, - 0x4cbf2baab8740924, - 0xc5008c6ec6592c89, - 0xecc2c57b472a9c2d, - 0x8613eafd9d81ffb1, - 0x10fe54daa2d3d495, - ]), - }, - y: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x7de7edc43953b75c, - 0x58be1d2de35e87dc, - 0x5731d30b0e337b40, - 0xbe93b60cfeaae4c9, - 0x8b22c203764bedca, - 0x01616c8d1033b771, - ]), - c1: Fp::from_raw_unchecked([ - 0xea126fe476b5733b, - 0x85cee68b5dae1652, - 0x98247779f7272b04, - 0xa649c8b468c6e808, - 0xb5b9a62dff0c4e45, - 0x1555b67fc7bbe73d, - ]), - }, - z: Fp2 { - c0: Fp::from_raw_unchecked([ - 0x0ef2ddffab187c0a, - 0x2424522b7d5ecbfc, - 0xc6f341a3398054f4, - 0x5523ddf409502df0, - 0xd55c0b5a88e0dd97, - 0x066428d704923e52, - ]), - c1: Fp::from_raw_unchecked([ - 0x538bbe0c95b4878d, - 0xad04a50379522881, - 0x6d5c05bf5c12fb64, - 0x4ce4a069a2d34787, - 0x59ea6c8d0dffaeaf, - 0x0d42a083a75bd6f3, - ]), - }, - }; - - assert!(bool::from(point.is_on_curve())); - assert!(!bool::from(G2Affine::from(point).is_torsion_free())); - let cleared_point = point.clear_cofactor(); - - assert!(bool::from(cleared_point.is_on_curve())); - assert!(bool::from(G2Affine::from(cleared_point).is_torsion_free())); - - // the generator (and the identity) are always on the curve, - // even after clearing the cofactor - let generator = G2Projective::generator(); - assert!(bool::from(generator.clear_cofactor().is_on_curve())); - let id = G2Projective::identity(); - assert!(bool::from(id.clear_cofactor().is_on_curve())); - - // test the effect on q-torsion points multiplying by h_eff modulo |Scalar| - // h_eff % q = 0x2b116900400069009a40200040001ffff - let h_eff_modq: [u8; 32] = [ - 0xff, 0xff, 0x01, 0x00, 0x04, 0x00, 0x02, 0xa4, 0x09, 0x90, 0x06, 0x00, 0x04, 0x90, 0x16, - 0xb1, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]; - assert_eq!(generator.clear_cofactor(), generator.multiply(&h_eff_modq)); - assert_eq!( - cleared_point.clear_cofactor(), - cleared_point.multiply(&h_eff_modq) - ); -} - -#[test] -fn test_batch_normalize() { - let a = G2Projective::generator().double(); - let b = a.double(); - let c = b.double(); - - for a_identity in (0..1).map(|n| n == 1) { - for b_identity in (0..1).map(|n| n == 1) { - for c_identity in (0..1).map(|n| n == 1) { - let mut v = [a, b, c]; - if a_identity { - v[0] = G2Projective::identity() - } - if b_identity { - v[1] = G2Projective::identity() - } - if c_identity { - v[2] = G2Projective::identity() - } - - let mut t = [ - G2Affine::identity(), - G2Affine::identity(), - G2Affine::identity(), - ]; - let expected = [ - G2Affine::from(v[0]), - G2Affine::from(v[1]), - G2Affine::from(v[2]), - ]; - - G2Projective::batch_normalize(&v[..], &mut t[..]); - - assert_eq!(&t[..], &expected[..]); - } - } - } -} diff --git a/bls12_381/src/lib.rs b/bls12_381/src/lib.rs deleted file mode 100644 index 7f16f1b47..000000000 --- a/bls12_381/src/lib.rs +++ /dev/null @@ -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}; diff --git a/bls12_381/src/notes/design.rs b/bls12_381/src/notes/design.rs deleted file mode 100644 index d273743b5..000000000 --- a/bls12_381/src/notes/design.rs +++ /dev/null @@ -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. = 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 -//! ``` diff --git a/bls12_381/src/notes/serialization.rs b/bls12_381/src/notes/serialization.rs deleted file mode 100644 index ded752e6e..000000000 --- a/bls12_381/src/notes/serialization.rs +++ /dev/null @@ -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. diff --git a/bls12_381/src/pairings.rs b/bls12_381/src/pairings.rs deleted file mode 100644 index 6e9cd4b89..000000000 --- a/bls12_381/src/pairings.rs +++ /dev/null @@ -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 Sum for Gt -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl Group for Gt { - type Scalar = Scalar; - - fn random(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 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(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); -} diff --git a/bls12_381/src/scalar.rs b/bls12_381/src/scalar.rs deleted file mode 100644 index c9c385d2d..000000000 --- a/bls12_381/src/scalar.rs +++ /dev/null @@ -1,1174 +0,0 @@ -//! This module provides an implementation of the BLS12-381 scalar field $\mathbb{F}_q$ -//! where `q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001` - -use core::convert::TryFrom; -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use rand_core::RngCore; - -use ff::{Field, PrimeField}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::util::{adc, mac, sbb}; - -/// Represents an element of the scalar field $\mathbb{F}_q$ of the BLS12-381 elliptic -/// curve construction. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. `Scalar` values are always in -// Montgomery form; i.e., Scalar(a) = aR mod q, with R = 2^256. -#[derive(Clone, Copy, Eq)] -pub struct Scalar(pub(crate) [u64; 4]); - -impl fmt::Debug for Scalar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_bytes(); - write!(f, "0x")?; - for &b in tmp.iter().rev() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } -} - -impl fmt::Display for Scalar { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl From for Scalar { - fn from(val: u64) -> Scalar { - Scalar([val, 0, 0, 0]) * R2 - } -} - -impl ConstantTimeEq for Scalar { - 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]) - } -} - -impl PartialEq for Scalar { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl ConditionallySelectable for Scalar { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Scalar([ - 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), - ]) - } -} - -/// Constant representing the modulus -/// q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 -const MODULUS: Scalar = Scalar([ - 0xffff_ffff_0000_0001, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, -]); - -const MODULUS_BYTES: [u8; 32] = [ - 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, 0xbd, 0x53, - 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29, 0x53, 0xa7, 0xed, 0x73, -]; - -// The number of bits needed to represent the modulus. -const MODULUS_BITS: u32 = 255; - -// GENERATOR = 7 (multiplicative generator of r-1 order, that is also quadratic nonresidue) -const GENERATOR: Scalar = Scalar([ - 0x0000_000e_ffff_fff1, - 0x17e3_63d3_0018_9c0f, - 0xff9c_5787_6f84_57b0, - 0x3513_3220_8fc5_a8c4, -]); - -impl<'a> Neg for &'a Scalar { - type Output = Scalar; - - #[inline] - fn neg(self) -> Scalar { - self.neg() - } -} - -impl Neg for Scalar { - type Output = Scalar; - - #[inline] - fn neg(self) -> Scalar { - -&self - } -} - -impl<'a, 'b> Sub<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn sub(self, rhs: &'b Scalar) -> Scalar { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn add(self, rhs: &'b Scalar) -> Scalar { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Scalar> for &'a Scalar { - type Output = Scalar; - - #[inline] - fn mul(self, rhs: &'b Scalar) -> Scalar { - self.mul(rhs) - } -} - -impl_binops_additive!(Scalar, Scalar); -impl_binops_multiplicative!(Scalar, Scalar); - -/// INV = -(q^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0xffff_fffe_ffff_ffff; - -/// R = 2^256 mod q -const R: Scalar = Scalar([ - 0x0000_0001_ffff_fffe, - 0x5884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff5, - 0x1824_b159_acc5_056f, -]); - -/// R^2 = 2^512 mod q -const R2: Scalar = Scalar([ - 0xc999_e990_f3f2_9c6d, - 0x2b6c_edcb_8792_5c23, - 0x05d3_1496_7254_398f, - 0x0748_d9d9_9f59_ff11, -]); - -/// R^3 = 2^768 mod q -const R3: Scalar = Scalar([ - 0xc62c_1807_439b_73af, - 0x1b3e_0d18_8cf0_6990, - 0x73d1_3c71_c7b5_f418, - 0x6e2a_5bb9_c8db_33e9, -]); - -// 2^S * t = MODULUS - 1 with t odd -const S: u32 = 32; - -/// GENERATOR^t where t * 2^s + 1 = q -/// with t odd. In other words, this -/// is a 2^s root of unity. -/// -/// `GENERATOR = 7 mod q` is a generator -/// of the q - 1 order multiplicative -/// subgroup. -const ROOT_OF_UNITY: Scalar = Scalar([ - 0xb9b5_8d8c_5f0e_466a, - 0x5b1b_4c80_1819_d7ec, - 0x0af5_3ae3_52a3_1e64, - 0x5bf3_adda_19e9_b27b, -]); - -impl Default for Scalar { - #[inline] - fn default() -> Self { - Self::zero() - } -} - -impl Scalar { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> Scalar { - Scalar([0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> Scalar { - R - } - - /// Doubles this field element. - #[inline] - pub const fn double(&self) -> Scalar { - // TODO: This can be achieved more efficiently with a bitshift. - self.add(self) - } - - /// Attempts to convert a little-endian byte representation of - /// a scalar into a `Scalar`, failing if the input is not canonical. - pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - let mut tmp = Scalar([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()); - tmp.0[1] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()); - tmp.0[2] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()); - tmp.0[3] = u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], 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 `Scalar` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 32] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Scalar::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - let mut res = [0; 32]; - res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); - - res - } - - /// Converts a 512-bit little endian integer into - /// a `Scalar` by reducing by the modulus. - pub fn from_bytes_wide(bytes: &[u8; 64]) -> Scalar { - Scalar::from_u512([ - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[0..8]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[8..16]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[16..24]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[24..32]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[32..40]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[40..48]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[48..56]).unwrap()), - u64::from_le_bytes(<[u8; 8]>::try_from(&bytes[56..64]).unwrap()), - ]) - } - - fn from_u512(limbs: [u64; 8]) -> Scalar { - // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits - // with the higher bits multiplied by 2^256. 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^256 = R^3 - // - // and computing their sum in the field. It remains to see that arbitrary 256-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^256 multiplied by - // the modulus. This holds because for any `c` smaller than the modulus, we have - // that (2^256 - 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 d0 = Scalar([limbs[0], limbs[1], limbs[2], limbs[3]]); - let d1 = Scalar([limbs[4], limbs[5], limbs[6], limbs[7]]); - // Convert to Montgomery form - d0 * R2 + d1 * R3 - } - - /// Converts from an integer represented in little endian - /// into its (congruent) `Scalar` representation. - pub const fn from_raw(val: [u64; 4]) -> Self { - (&Scalar(val)).mul(&R2) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> Scalar { - let (r1, carry) = mac(0, self.0[0], self.0[1], 0); - let (r2, carry) = mac(0, self.0[0], self.0[2], carry); - let (r3, r4) = mac(0, self.0[0], self.0[3], carry); - - let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); - let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); - - let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); - - let r7 = r6 >> 63; - let r6 = (r6 << 1) | (r5 >> 63); - let r5 = (r5 << 1) | (r4 >> 63); - let r4 = (r4 << 1) | (r3 >> 63); - let r3 = (r3 << 1) | (r2 >> 63); - let r2 = (r2 << 1) | (r1 >> 63); - let r1 = r1 << 1; - - let (r0, carry) = mac(0, self.0[0], self.0[0], 0); - let (r1, carry) = adc(0, r1, carry); - let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); - let (r3, carry) = adc(0, r3, carry); - let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); - let (r5, carry) = adc(0, r5, carry); - let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); - let (r7, _) = adc(0, r7, carry); - - Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Computes the square root of this element, if it exists. - pub fn sqrt(&self) -> CtOption { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - - // w = self^((t - 1) // 2) - // = self^6104339283789297388802252303364915521546564123189034618274734669823 - let w = self.pow_vartime(&[ - 0x7fff_2dff_7fff_ffff, - 0x04d0_ec02_a9de_d201, - 0x94ce_bea4_199c_ec04, - 0x0000_0000_39f6_d3a9, - ]); - - let mut v = S; - let mut x = self * w; - let mut b = x * w; - - // Initialize z as the 2^S root of unity. - let mut z = ROOT_OF_UNITY; - - for max_v in (1..=S).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&Scalar::one()); - let squared = Scalar::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = Scalar::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = Scalar::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = Scalar::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * z; - x = Scalar::conditional_select(&result, &x, b.ct_eq(&Scalar::one())); - z = z.square(); - b *= z; - v = k; - } - - CtOption::new( - x, - (x * x).ct_eq(self), // Only return Some if it's the square root. - ) - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - pub fn pow(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - let mut tmp = res; - tmp *= self; - res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); - } - } - res - } - - /// Exponentiates `self` by `by`, where `by` 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. - pub fn pow_vartime(&self, by: &[u64; 4]) -> 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.mul_assign(self); - } - } - } - res - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - pub fn invert(&self) -> CtOption { - #[inline(always)] - fn square_assign_multi(n: &mut Scalar, num_times: usize) { - for _ in 0..num_times { - *n = n.square(); - } - } - // found using https://github.com/kwantam/addchain - let mut t0 = self.square(); - let mut t1 = t0 * self; - let mut t16 = t0.square(); - let mut t6 = t16.square(); - let mut t5 = t6 * t0; - t0 = t6 * t16; - let mut t12 = t5 * t16; - let mut t2 = t6.square(); - let mut t7 = t5 * t6; - let mut t15 = t0 * t5; - let mut t17 = t12.square(); - t1 *= t17; - let mut t3 = t7 * t2; - let t8 = t1 * t17; - let t4 = t8 * t2; - let t9 = t8 * t7; - t7 = t4 * t5; - let t11 = t4 * t17; - t5 = t9 * t17; - let t14 = t7 * t15; - let t13 = t11 * t12; - t12 = t11 * t17; - t15 *= &t12; - t16 *= &t15; - t3 *= &t16; - t17 *= &t3; - t0 *= &t17; - t6 *= &t0; - t2 *= &t6; - square_assign_multi(&mut t0, 8); - t0 *= &t17; - square_assign_multi(&mut t0, 9); - t0 *= &t16; - square_assign_multi(&mut t0, 9); - t0 *= &t15; - square_assign_multi(&mut t0, 9); - t0 *= &t15; - square_assign_multi(&mut t0, 7); - t0 *= &t14; - square_assign_multi(&mut t0, 7); - t0 *= &t13; - square_assign_multi(&mut t0, 10); - t0 *= &t12; - square_assign_multi(&mut t0, 9); - t0 *= &t11; - square_assign_multi(&mut t0, 8); - t0 *= &t8; - square_assign_multi(&mut t0, 8); - t0 *= self; - square_assign_multi(&mut t0, 14); - t0 *= &t9; - square_assign_multi(&mut t0, 10); - t0 *= &t8; - square_assign_multi(&mut t0, 15); - t0 *= &t7; - square_assign_multi(&mut t0, 10); - t0 *= &t6; - square_assign_multi(&mut t0, 8); - t0 *= &t5; - square_assign_multi(&mut t0, 16); - t0 *= &t3; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 7); - t0 *= &t4; - square_assign_multi(&mut t0, 9); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t3; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t3; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 8); - t0 *= &t2; - square_assign_multi(&mut t0, 5); - t0 *= &t1; - square_assign_multi(&mut t0, 5); - t0 *= &t1; - - CtOption::new(t0, !self.ct_eq(&Self::zero())) - } - - #[inline(always)] - const fn montgomery_reduce( - r0: u64, - r1: u64, - r2: u64, - r3: u64, - r4: u64, - r5: u64, - r6: u64, - r7: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = r0.wrapping_mul(INV); - let (_, carry) = mac(r0, k, MODULUS.0[0], 0); - let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); - let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); - let (r4, carry2) = adc(r4, 0, carry); - - let k = r1.wrapping_mul(INV); - let (_, carry) = mac(r1, k, MODULUS.0[0], 0); - let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); - let (r5, carry2) = adc(r5, carry2, carry); - - let k = r2.wrapping_mul(INV); - let (_, carry) = mac(r2, k, MODULUS.0[0], 0); - let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); - let (r6, carry2) = adc(r6, carry2, carry); - - let k = r3.wrapping_mul(INV); - let (_, carry) = mac(r3, k, MODULUS.0[0], 0); - let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); - let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); - let (r7, _) = adc(r7, carry2, carry); - - // Result may be within MODULUS of the correct value - (&Scalar([r4, r5, r6, r7])).sub(&MODULUS) - } - - /// Multiplies `rhs` by `self`, returning the result. - #[inline] - pub const fn mul(&self, rhs: &Self) -> Self { - // Schoolbook multiplication - - let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); - let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); - let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); - let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); - - let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); - let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); - let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); - let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); - - let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); - let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); - let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); - let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); - - let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); - let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); - let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); - let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); - - Scalar::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Subtracts `rhs` from `self`, returning the result. - #[inline] - pub const fn sub(&self, rhs: &Self) -> Self { - let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); - let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); - let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); - let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); - - // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise - // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. - let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); - let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); - let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); - let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); - - Scalar([d0, d1, d2, d3]) - } - - /// Adds `rhs` to `self`, returning the result. - #[inline] - pub const fn add(&self, rhs: &Self) -> Self { - 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, _) = adc(self.0[3], rhs.0[3], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Scalar([d0, d1, d2, d3])).sub(&MODULUS) - } - - /// Negates `self`. - #[inline] - pub const fn neg(&self) -> Self { - // Subtract `self` from `MODULUS` to negate. Ignore the final - // borrow because it cannot underflow; self is guaranteed to - // be in the field. - let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); - let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); - let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); - let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); - - // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is - // zero if `self` was zero, and `u64::max_value()` if self was nonzero. - let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); - - Scalar([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) - } -} - -impl From for [u8; 32] { - fn from(value: Scalar) -> [u8; 32] { - value.to_bytes() - } -} - -impl<'a> From<&'a Scalar> for [u8; 32] { - fn from(value: &'a Scalar) -> [u8; 32] { - value.to_bytes() - } -} - -impl Field for Scalar { - fn random(rng: &mut R) -> Self { - let mut buf = [0; 64]; - rng.fill_bytes(&mut buf); - Self::from_bytes_wide(&buf) - } - - fn zero() -> Self { - Self::zero() - } - - fn one() -> Self { - Self::one() - } - - fn is_zero(&self) -> bool { - self.ct_eq(&Self::zero()).into() - } - - #[must_use] - fn square(&self) -> Self { - self.square() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } - - fn invert(&self) -> CtOption { - self.invert() - } - - fn sqrt(&self) -> CtOption { - self.sqrt() - } -} - -impl PrimeField for Scalar { - type Repr = [u8; 32]; - type ReprEndianness = byteorder::LittleEndian; - - fn from_repr(r: Self::Repr) -> Option { - let res = Self::from_bytes(&r); - if res.is_some().into() { - Some(res.unwrap()) - } else { - None - } - } - - fn to_repr(&self) -> Self::Repr { - self.to_bytes() - } - - fn is_odd(&self) -> bool { - self.to_bytes()[0] & 1 == 1 - } - - fn char() -> Self::Repr { - MODULUS_BYTES - } - - const NUM_BITS: u32 = MODULUS_BITS; - const CAPACITY: u32 = Self::NUM_BITS - 1; - - fn multiplicative_generator() -> Self { - GENERATOR - } - - const S: u32 = S; - - fn root_of_unity() -> Self { - ROOT_OF_UNITY - } -} - -#[test] -fn test_inv() { - // Compute -(q^{-1} mod 2^64) mod 2^64 by exponentiating - // by totient(2**64) - 1 - - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(MODULUS.0[0]); - } - inv = inv.wrapping_neg(); - - assert_eq!(inv, INV); -} - -#[cfg(feature = "std")] -#[test] -fn test_debug() { - assert_eq!( - format!("{:?}", Scalar::zero()), - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format!("{:?}", Scalar::one()), - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - assert_eq!( - format!("{:?}", R2), - "0x1824b159acc5056f998c4fefecbc4ff55884b7fa0003480200000001fffffffe" - ); -} - -#[test] -fn test_equality() { - assert_eq!(Scalar::zero(), Scalar::zero()); - assert_eq!(Scalar::one(), Scalar::one()); - assert_eq!(R2, R2); - - assert!(Scalar::zero() != Scalar::one()); - assert!(Scalar::one() != R2); -} - -#[test] -fn test_to_bytes() { - assert_eq!( - Scalar::zero().to_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - Scalar::one().to_bytes(), - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - R2.to_bytes(), - [ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - ] - ); - - assert_eq!( - (-&Scalar::one()).to_bytes(), - [ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ] - ); -} - -#[test] -fn test_from_bytes() { - assert_eq!( - Scalar::from_bytes(&[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Scalar::zero() - ); - - assert_eq!( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Scalar::one() - ); - - assert_eq!( - Scalar::from_bytes(&[ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24 - ]) - .unwrap(), - R2 - ); - - // -1 should work - assert!(bool::from( - Scalar::from_bytes(&[ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_some() - )); - - // modulus is invalid - assert!(bool::from( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_none() - )); - - // Anything larger than the modulus is invalid - assert!(bool::from( - Scalar::from_bytes(&[ - 2, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_none() - )); - assert!(bool::from( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 58, 51, 72, 125, 157, 41, 83, 167, 237, 115 - ]) - .is_none() - )); - assert!(bool::from( - Scalar::from_bytes(&[ - 1, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 116 - ]) - .is_none() - )); -} - -#[test] -fn test_from_u512_zero() { - assert_eq!( - Scalar::zero(), - Scalar::from_u512([ - MODULUS.0[0], - MODULUS.0[1], - MODULUS.0[2], - MODULUS.0[3], - 0, - 0, - 0, - 0 - ]) - ); -} - -#[test] -fn test_from_u512_r() { - assert_eq!(R, Scalar::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); -} - -#[test] -fn test_from_u512_r2() { - assert_eq!(R2, Scalar::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); -} - -#[test] -fn test_from_u512_max() { - let max_u64 = 0xffff_ffff_ffff_ffff; - assert_eq!( - R3 - R, - Scalar::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) - ); -} - -#[test] -fn test_from_bytes_wide_r2() { - assert_eq!( - R2, - Scalar::from_bytes_wide(&[ - 254, 255, 255, 255, 1, 0, 0, 0, 2, 72, 3, 0, 250, 183, 132, 88, 245, 79, 188, 236, 239, - 79, 140, 153, 111, 5, 197, 172, 89, 177, 36, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_negative_one() { - assert_eq!( - -&Scalar::one(), - Scalar::from_bytes_wide(&[ - 0, 0, 0, 0, 255, 255, 255, 255, 254, 91, 254, 255, 2, 164, 189, 83, 5, 216, 161, 9, 8, - 216, 57, 51, 72, 125, 157, 41, 83, 167, 237, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_maximum() { - assert_eq!( - Scalar([ - 0xc62c_1805_439b_73b1, - 0xc2b9_551e_8ced_218e, - 0xda44_ec81_daf9_a422, - 0x5605_aa60_1c16_2e79, - ]), - Scalar::from_bytes_wide(&[0xff; 64]) - ); -} - -#[test] -fn test_zero() { - assert_eq!(Scalar::zero(), -&Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() + Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() - Scalar::zero()); - assert_eq!(Scalar::zero(), Scalar::zero() * Scalar::zero()); -} - -#[cfg(test)] -const LARGEST: Scalar = Scalar([ - 0xffff_ffff_0000_0000, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, -]); - -#[test] -fn test_addition() { - let mut tmp = LARGEST; - tmp += &LARGEST; - - assert_eq!( - tmp, - Scalar([ - 0xffff_fffe_ffff_ffff, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, - ]) - ); - - let mut tmp = LARGEST; - tmp += &Scalar([1, 0, 0, 0]); - - assert_eq!(tmp, Scalar::zero()); -} - -#[test] -fn test_negation() { - let tmp = -&LARGEST; - - assert_eq!(tmp, Scalar([1, 0, 0, 0])); - - let tmp = -&Scalar::zero(); - assert_eq!(tmp, Scalar::zero()); - let tmp = -&Scalar([1, 0, 0, 0]); - assert_eq!(tmp, LARGEST); -} - -#[test] -fn test_subtraction() { - let mut tmp = LARGEST; - tmp -= &LARGEST; - - assert_eq!(tmp, Scalar::zero()); - - let mut tmp = Scalar::zero(); - tmp -= &LARGEST; - - let mut tmp2 = MODULUS; - tmp2 -= &LARGEST; - - assert_eq!(tmp, tmp2); -} - -#[test] -fn test_multiplication() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp *= &cur; - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_squaring() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp = tmp.square(); - - let mut tmp2 = Scalar::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_inversion() { - assert!(bool::from(Scalar::zero().invert().is_none())); - assert_eq!(Scalar::one().invert().unwrap(), Scalar::one()); - assert_eq!((-&Scalar::one()).invert().unwrap(), -&Scalar::one()); - - let mut tmp = R2; - - for _ in 0..100 { - let mut tmp2 = tmp.invert().unwrap(); - tmp2.mul_assign(&tmp); - - assert_eq!(tmp2, Scalar::one()); - - tmp.add_assign(&R2); - } -} - -#[test] -fn test_invert_is_pow() { - let q_minus_2 = [ - 0xffff_fffe_ffff_ffff, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, - ]; - - let mut r1 = R; - let mut r2 = R; - let mut r3 = R; - - for _ in 0..100 { - r1 = r1.invert().unwrap(); - r2 = r2.pow_vartime(&q_minus_2); - r3 = r3.pow(&q_minus_2); - - assert_eq!(r1, r2); - assert_eq!(r2, r3); - // Add R so we check something different next time around - r1.add_assign(&R); - r2 = r1; - r3 = r1; - } -} - -#[test] -fn test_sqrt() { - { - assert_eq!(Scalar::zero().sqrt().unwrap(), Scalar::zero()); - } - - let mut square = Scalar([ - 0x46cd_85a5_f273_077e, - 0x1d30_c47d_d68f_c735, - 0x77f6_56f6_0bec_a0eb, - 0x494a_a01b_df32_468d, - ]); - - let mut none_count = 0; - - for _ in 0..100 { - let square_root = square.sqrt(); - if bool::from(square_root.is_none()) { - none_count += 1; - } else { - assert_eq!(square_root.unwrap() * square_root.unwrap(), square); - } - square -= Scalar::one(); - } - - assert_eq!(49, none_count); -} - -#[test] -fn test_from_raw() { - assert_eq!( - Scalar::from_raw([ - 0x0001_ffff_fffd, - 0x5884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff5, - 0x1824_b159_acc5_056f, - ]), - Scalar::from_raw([0xffff_ffff_ffff_ffff; 4]) - ); - - assert_eq!(Scalar::from_raw(MODULUS.0), Scalar::zero()); - - assert_eq!(Scalar::from_raw([1, 0, 0, 0]), R); -} - -#[test] -fn test_double() { - let a = Scalar::from_raw([ - 0x1fff_3231_233f_fffd, - 0x4884_b7fa_0003_4802, - 0x998c_4fef_ecbc_4ff3, - 0x1824_b159_acc5_0562, - ]); - - assert_eq!(a.double(), a + a); -} diff --git a/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat deleted file mode 100644 index ea8cd6765..000000000 Binary files a/bls12_381/src/tests/g1_compressed_valid_test_vectors.dat and /dev/null differ diff --git a/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 86abfba94..000000000 Binary files a/bls12_381/src/tests/g1_uncompressed_valid_test_vectors.dat and /dev/null differ diff --git a/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat deleted file mode 100644 index a40bbe251..000000000 Binary files a/bls12_381/src/tests/g2_compressed_valid_test_vectors.dat and /dev/null differ diff --git a/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat b/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat deleted file mode 100644 index 92e4bc528..000000000 Binary files a/bls12_381/src/tests/g2_uncompressed_valid_test_vectors.dat and /dev/null differ diff --git a/bls12_381/src/tests/mod.rs b/bls12_381/src/tests/mod.rs deleted file mode 100644 index d6ab86d28..000000000 --- a/bls12_381/src/tests/mod.rs +++ /dev/null @@ -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, - ]) - } - } - } - ); -} diff --git a/bls12_381/src/util.rs b/bls12_381/src/util.rs deleted file mode 100644 index bd25dd06a..000000000 --- a/bls12_381/src/util.rs +++ /dev/null @@ -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; - } - } - }; -} diff --git a/ff/.gitignore b/ff/.gitignore deleted file mode 100644 index 4308d8220..000000000 --- a/ff/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -target/ -**/*.rs.bk -Cargo.lock diff --git a/ff/Cargo.toml b/ff/Cargo.toml deleted file mode 100644 index e081e84c1..000000000 --- a/ff/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "ff" -version = "0.7.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -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" } diff --git a/ff/LICENSE-APACHE b/ff/LICENSE-APACHE deleted file mode 100644 index 1e5006dc1..000000000 --- a/ff/LICENSE-APACHE +++ /dev/null @@ -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. - diff --git a/ff/LICENSE-MIT b/ff/LICENSE-MIT deleted file mode 100644 index ed3a13fdd..000000000 --- a/ff/LICENSE-MIT +++ /dev/null @@ -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. diff --git a/ff/README.md b/ff/README.md deleted file mode 100644 index 4fbe19028..000000000 --- a/ff/README.md +++ /dev/null @@ -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. diff --git a/ff/ff_derive/Cargo.toml b/ff/ff_derive/Cargo.toml deleted file mode 100644 index e1e26bc56..000000000 --- a/ff/ff_derive/Cargo.toml +++ /dev/null @@ -1,28 +0,0 @@ -[package] -name = "ff_derive" -version = "0.7.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -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" } diff --git a/ff/ff_derive/src/lib.rs b/ff/ff_derive/src/lib.rs deleted file mode 100644 index c5c811ab3..000000000 --- a/ff/ff_derive/src/lib.rs +++ /dev/null @@ -1,1328 +0,0 @@ -#![recursion_limit = "1024"] - -extern crate proc_macro; -extern crate proc_macro2; - -use num_bigint::BigUint; -use num_integer::Integer; -use num_traits::{One, ToPrimitive, Zero}; -use quote::quote; -use quote::TokenStreamExt; -use std::iter; -use std::str::FromStr; - -mod pow_fixed; - -enum ReprEndianness { - Big, - Little, -} - -impl FromStr for ReprEndianness { - type Err = (); - - fn from_str(s: &str) -> Result { - match s { - "big" => Ok(ReprEndianness::Big), - "little" => Ok(ReprEndianness::Little), - _ => Err(()), - } - } -} - -impl ReprEndianness { - fn repr_endianness(&self) -> proc_macro2::TokenStream { - match self { - ReprEndianness::Big => quote! {::byteorder::BigEndian}, - ReprEndianness::Little => quote! {::byteorder::LittleEndian}, - } - } - - fn modulus_repr(&self, modulus: &BigUint, bytes: usize) -> Vec { - match self { - ReprEndianness::Big => { - let buf = modulus.to_bytes_be(); - iter::repeat(0) - .take(bytes - buf.len()) - .chain(buf.into_iter()) - .collect() - } - ReprEndianness::Little => { - let mut buf = modulus.to_bytes_le(); - buf.extend(iter::repeat(0).take(bytes - buf.len())); - buf - } - } - } - - fn from_repr(&self, name: &syn::Ident, limbs: usize) -> proc_macro2::TokenStream { - let read_repr = match self { - ReprEndianness::Big => quote! { - ::byteorder::BigEndian::read_u64_into(r.as_ref(), &mut inner[..]); - inner.reverse(); - }, - ReprEndianness::Little => quote! { - ::byteorder::LittleEndian::read_u64_into(r.as_ref(), &mut inner[..]); - }, - }; - - quote! { - use ::byteorder::ByteOrder; - - let r = { - let mut inner = [0u64; #limbs]; - #read_repr - #name(inner) - }; - - if r.is_valid() { - Some(r * R2) - } else { - None - } - } - } - - fn to_repr( - &self, - repr: &syn::Ident, - mont_reduce_self_params: &proc_macro2::TokenStream, - limbs: usize, - ) -> proc_macro2::TokenStream { - let bytes = limbs * 8; - - let write_repr = match self { - ReprEndianness::Big => quote! { - r.0.reverse(); - ::byteorder::BigEndian::write_u64_into(&r.0, &mut repr[..]); - }, - ReprEndianness::Little => quote! { - ::byteorder::LittleEndian::write_u64_into(&r.0, &mut repr[..]); - }, - }; - - quote! { - use ::byteorder::ByteOrder; - - let mut r = *self; - r.mont_reduce( - #mont_reduce_self_params - ); - - let mut repr = [0u8; #bytes]; - #write_repr - #repr(repr) - } - } - - fn iter_be(&self) -> proc_macro2::TokenStream { - match self { - ReprEndianness::Big => quote! {self.0.iter()}, - ReprEndianness::Little => quote! {self.0.iter().rev()}, - } - } -} - -#[proc_macro_derive( - PrimeField, - attributes(PrimeFieldModulus, PrimeFieldGenerator, PrimeFieldReprEndianness) -)] -pub fn prime_field(input: proc_macro::TokenStream) -> proc_macro::TokenStream { - // Parse the type definition - let ast: syn::DeriveInput = syn::parse(input).unwrap(); - - // We're given the modulus p of the prime field - let modulus: BigUint = fetch_attr("PrimeFieldModulus", &ast.attrs) - .expect("Please supply a PrimeFieldModulus attribute") - .parse() - .expect("PrimeFieldModulus should be a number"); - - // We may be provided with a generator of p - 1 order. It is required that this generator be quadratic - // nonresidue. - // TODO: Compute this ourselves. - let generator: BigUint = fetch_attr("PrimeFieldGenerator", &ast.attrs) - .expect("Please supply a PrimeFieldGenerator attribute") - .parse() - .expect("PrimeFieldGenerator should be a number"); - - // Field element representations may be in little-endian or big-endian. - let endianness = fetch_attr("PrimeFieldReprEndianness", &ast.attrs) - .expect("Please supply a PrimeFieldReprEndianness attribute") - .parse() - .expect("PrimeFieldReprEndianness should be 'big' or 'little'"); - - // The arithmetic in this library only works if the modulus*2 is smaller than the backing - // representation. Compute the number of limbs we need. - let mut limbs = 1; - { - let mod2 = (&modulus) << 1; // modulus * 2 - let mut cur = BigUint::one() << 64; // always 64-bit limbs for now - while cur < mod2 { - limbs += 1; - cur <<= 64; - } - } - - // The struct we're deriving for must be a wrapper around `pub [u64; limbs]`. - if let Some(err) = validate_struct(&ast, limbs) { - return err.into(); - } - - // Generate the identifier for the "Repr" type we must construct. - let repr_ident = syn::Ident::new( - &format!("{}Repr", ast.ident), - proc_macro2::Span::call_site(), - ); - - let mut gen = proc_macro2::TokenStream::new(); - - let (constants_impl, sqrt_impl) = prime_field_constants_and_sqrt( - &ast.ident, - &repr_ident, - &modulus, - &endianness, - limbs, - generator, - ); - - gen.extend(constants_impl); - gen.extend(prime_field_repr_impl(&repr_ident, &endianness, limbs * 8)); - gen.extend(prime_field_impl( - &ast.ident, - &repr_ident, - &modulus, - &endianness, - limbs, - sqrt_impl, - )); - - // Return the generated impl - gen.into() -} - -/// Checks that `body` contains `pub [u64; limbs]`. -fn validate_struct(ast: &syn::DeriveInput, limbs: usize) -> Option { - // The body should be a struct. - let variant_data = match &ast.data { - syn::Data::Struct(x) => x, - _ => { - return Some( - syn::Error::new_spanned(ast, "PrimeField derive only works for structs.") - .to_compile_error(), - ) - } - }; - - // The struct should contain a single unnamed field. - let fields = match &variant_data.fields { - syn::Fields::Unnamed(x) if x.unnamed.len() == 1 => x, - _ => { - return Some( - syn::Error::new_spanned( - &ast.ident, - format!( - "The struct must contain an array of limbs. Change this to `{}([u64; {}])`", - ast.ident, limbs, - ), - ) - .to_compile_error(), - ) - } - }; - let field = &fields.unnamed[0]; - - // The field should be an array. - let arr = match &field.ty { - syn::Type::Array(x) => x, - _ => { - return Some( - syn::Error::new_spanned( - field, - format!( - "The inner field must be an array of limbs. Change this to `[u64; {}]`", - limbs, - ), - ) - .to_compile_error(), - ) - } - }; - - // The array's element type should be `u64`. - if match arr.elem.as_ref() { - syn::Type::Path(path) => path - .path - .get_ident() - .map(|x| x.to_string() != "u64") - .unwrap_or(true), - _ => true, - } { - return Some( - syn::Error::new_spanned( - arr, - format!( - "PrimeField derive requires 64-bit limbs. Change this to `[u64; {}]", - limbs - ), - ) - .to_compile_error(), - ); - } - - // The array's length should be a literal int equal to `limbs`. - let lit_int = match match &arr.len { - syn::Expr::Lit(expr_lit) => match &expr_lit.lit { - syn::Lit::Int(lit_int) => Some(lit_int), - _ => None, - }, - _ => None, - } { - Some(x) => x, - _ => { - return Some( - syn::Error::new_spanned( - arr, - format!("To derive PrimeField, change this to `[u64; {}]`.", limbs), - ) - .to_compile_error(), - ) - } - }; - if lit_int.base10_digits() != limbs.to_string() { - return Some( - syn::Error::new_spanned( - lit_int, - format!("The given modulus requires {} limbs.", limbs), - ) - .to_compile_error(), - ); - } - - // The field should not be public. - match &field.vis { - syn::Visibility::Inherited => (), - _ => { - return Some( - syn::Error::new_spanned(&field.vis, "Field must not be public.").to_compile_error(), - ) - } - } - - // Valid! - None -} - -/// Fetch an attribute string from the derived struct. -fn fetch_attr(name: &str, attrs: &[syn::Attribute]) -> Option { - for attr in attrs { - if let Ok(meta) = attr.parse_meta() { - match meta { - syn::Meta::NameValue(nv) => { - if nv.path.get_ident().map(|i| i.to_string()) == Some(name.to_string()) { - match nv.lit { - syn::Lit::Str(ref s) => return Some(s.value()), - _ => { - panic!("attribute {} should be a string", name); - } - } - } - } - _ => { - panic!("attribute {} should be a string", name); - } - } - } - } - - None -} - -// Implement the wrapped ident `repr` with `bytes` bytes. -fn prime_field_repr_impl( - repr: &syn::Ident, - endianness: &ReprEndianness, - bytes: usize, -) -> proc_macro2::TokenStream { - let repr_iter_be = endianness.iter_be(); - - quote! { - #[derive(Copy, Clone)] - pub struct #repr(pub [u8; #bytes]); - - impl ::subtle::ConstantTimeEq for #repr { - fn ct_eq(&self, other: &#repr) -> ::subtle::Choice { - self.0 - .iter() - .zip(other.0.iter()) - .map(|(a, b)| a.ct_eq(b)) - .fold(1.into(), |acc, x| acc & x) - } - } - - impl ::core::cmp::PartialEq for #repr { - fn eq(&self, other: &#repr) -> bool { - use ::subtle::ConstantTimeEq; - self.ct_eq(other).into() - } - } - - impl ::core::cmp::Eq for #repr { } - - impl ::core::default::Default for #repr { - fn default() -> #repr { - #repr([0u8; #bytes]) - } - } - - impl ::core::fmt::Debug for #repr - { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "0x")?; - for i in #repr_iter_be { - write!(f, "{:02x}", *i)?; - } - - Ok(()) - } - } - - impl ::core::fmt::Display for #repr { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "0x")?; - for i in #repr_iter_be { - write!(f, "{:02x}", *i)?; - } - - Ok(()) - } - } - - impl AsRef<[u8]> for #repr { - #[inline(always)] - fn as_ref(&self) -> &[u8] { - &self.0 - } - } - - impl AsMut<[u8]> for #repr { - #[inline(always)] - fn as_mut(&mut self) -> &mut [u8] { - &mut self.0 - } - } - } -} - -/// Convert BigUint into a vector of 64-bit limbs. -fn biguint_to_real_u64_vec(mut v: BigUint, limbs: usize) -> Vec { - let m = BigUint::one() << 64; - let mut ret = vec![]; - - while v > BigUint::zero() { - let limb: BigUint = &v % &m; - ret.push(limb.to_u64().unwrap()); - v >>= 64; - } - - while ret.len() < limbs { - ret.push(0); - } - - assert!(ret.len() == limbs); - - ret -} - -/// Convert BigUint into a tokenized vector of 64-bit limbs. -fn biguint_to_u64_vec(v: BigUint, limbs: usize) -> proc_macro2::TokenStream { - let ret = biguint_to_real_u64_vec(v, limbs); - quote!([#(#ret,)*]) -} - -fn biguint_num_bits(mut v: BigUint) -> u32 { - let mut bits = 0; - - while v != BigUint::zero() { - v >>= 1; - bits += 1; - } - - bits -} - -/// BigUint modular exponentiation by square-and-multiply. -fn exp(base: BigUint, exp: &BigUint, modulus: &BigUint) -> BigUint { - let mut ret = BigUint::one(); - - for i in exp - .to_bytes_be() - .into_iter() - .flat_map(|x| (0..8).rev().map(move |i| (x >> i).is_odd())) - { - ret = (&ret * &ret) % modulus; - if i { - ret = (ret * &base) % modulus; - } - } - - ret -} - -#[test] -fn test_exp() { - assert_eq!( - exp( - BigUint::from_str("4398572349857239485729348572983472345").unwrap(), - &BigUint::from_str("5489673498567349856734895").unwrap(), - &BigUint::from_str( - "52435875175126190479447740508185965837690552500527637822603658699938581184513" - ) - .unwrap() - ), - BigUint::from_str( - "4371221214068404307866768905142520595925044802278091865033317963560480051536" - ) - .unwrap() - ); -} - -fn prime_field_constants_and_sqrt( - name: &syn::Ident, - repr: &syn::Ident, - modulus: &BigUint, - endianness: &ReprEndianness, - limbs: usize, - generator: BigUint, -) -> (proc_macro2::TokenStream, proc_macro2::TokenStream) { - let modulus_num_bits = biguint_num_bits(modulus.clone()); - - // The number of bits we should "shave" from a randomly sampled reputation, i.e., - // if our modulus is 381 bits and our representation is 384 bits, we should shave - // 3 bits from the beginning of a randomly sampled 384 bit representation to - // reduce the cost of rejection sampling. - let repr_shave_bits = (64 * limbs as u32) - biguint_num_bits(modulus.clone()); - - // Compute R = 2**(64 * limbs) mod m - let r = (BigUint::one() << (limbs * 64)) % modulus; - - // modulus - 1 = 2^s * t - let mut s: u32 = 0; - let mut t = modulus - BigUint::from_str("1").unwrap(); - while t.is_even() { - t >>= 1; - s += 1; - } - - // Compute 2^s root of unity given the generator - let root_of_unity = - biguint_to_u64_vec((exp(generator.clone(), &t, &modulus) * &r) % modulus, limbs); - let generator = biguint_to_u64_vec((generator.clone() * &r) % modulus, limbs); - - let sqrt_impl = - if (modulus % BigUint::from_str("4").unwrap()) == BigUint::from_str("3").unwrap() { - // Addition chain for (r + 1) // 4 - let mod_plus_1_over_4 = pow_fixed::generate( - "e! {self}, - (modulus + BigUint::from_str("1").unwrap()) >> 2, - ); - - quote! { - use ::subtle::ConstantTimeEq; - - // Because r = 3 (mod 4) - // sqrt can be done with only one exponentiation, - // via the computation of self^((r + 1) // 4) (mod r) - let sqrt = { - #mod_plus_1_over_4 - }; - - ::subtle::CtOption::new( - sqrt, - (sqrt * &sqrt).ct_eq(self), // Only return Some if it's the square root. - ) - } - } else if (modulus % BigUint::from_str("16").unwrap()) == BigUint::from_str("1").unwrap() { - // Addition chain for (t - 1) // 2 - let t_minus_1_over_2 = pow_fixed::generate("e! {self}, (&t - BigUint::one()) >> 1); - - quote! { - // Tonelli-Shank's algorithm for q mod 16 = 1 - // https://eprint.iacr.org/2012/685.pdf (page 12, algorithm 5) - use ::subtle::{ConditionallySelectable, ConstantTimeEq}; - - // w = self^((t - 1) // 2) - let w = { - #t_minus_1_over_2 - }; - - let mut v = S; - let mut x = *self * &w; - let mut b = x * &w; - - // Initialize z as the 2^S root of unity. - let mut z = ROOT_OF_UNITY; - - for max_v in (1..=S).rev() { - let mut k = 1; - let mut tmp = b.square(); - let mut j_less_than_v: ::subtle::Choice = 1.into(); - - for j in 2..max_v { - let tmp_is_one = tmp.ct_eq(&#name::one()); - let squared = #name::conditional_select(&tmp, &z, tmp_is_one).square(); - tmp = #name::conditional_select(&squared, &tmp, tmp_is_one); - let new_z = #name::conditional_select(&z, &squared, tmp_is_one); - j_less_than_v &= !j.ct_eq(&v); - k = u32::conditional_select(&j, &k, tmp_is_one); - z = #name::conditional_select(&z, &new_z, j_less_than_v); - } - - let result = x * &z; - x = #name::conditional_select(&result, &x, b.ct_eq(&#name::one())); - z = z.square(); - b *= &z; - v = k; - } - - ::subtle::CtOption::new( - x, - (x * &x).ct_eq(self), // Only return Some if it's the square root. - ) - } - } else { - syn::Error::new_spanned( - &name, - "ff_derive can't generate a square root function for this field.", - ) - .to_compile_error() - }; - - // Compute R^2 mod m - let r2 = biguint_to_u64_vec((&r * &r) % modulus, limbs); - - let r = biguint_to_u64_vec(r, limbs); - let modulus_repr = endianness.modulus_repr(modulus, limbs * 8); - let modulus = biguint_to_real_u64_vec(modulus.clone(), limbs); - - // Compute -m^-1 mod 2**64 by exponentiating by totient(2**64) - 1 - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(modulus[0]); - } - inv = inv.wrapping_neg(); - - ( - quote! { - /// This is the modulus m of the prime field - const MODULUS: #repr = #repr([#(#modulus_repr,)*]); - - /// This is the modulus m of the prime field in limb form - const MODULUS_LIMBS: #name = #name([#(#modulus,)*]); - - /// The number of bits needed to represent the modulus. - const MODULUS_BITS: u32 = #modulus_num_bits; - - /// The number of bits that must be shaved from the beginning of - /// the representation when randomly sampling. - const REPR_SHAVE_BITS: u32 = #repr_shave_bits; - - /// 2^{limbs*64} mod m - const R: #name = #name(#r); - - /// 2^{limbs*64*2} mod m - const R2: #name = #name(#r2); - - /// -(m^{-1} mod m) mod m - const INV: u64 = #inv; - - /// Multiplicative generator of `MODULUS` - 1 order, also quadratic - /// nonresidue. - const GENERATOR: #name = #name(#generator); - - /// 2^s * t = MODULUS - 1 with t odd - const S: u32 = #s; - - /// 2^s root of unity computed by GENERATOR^t - const ROOT_OF_UNITY: #name = #name(#root_of_unity); - }, - sqrt_impl, - ) -} - -/// Implement PrimeField for the derived type. -fn prime_field_impl( - name: &syn::Ident, - repr: &syn::Ident, - modulus: &BigUint, - endianness: &ReprEndianness, - limbs: usize, - sqrt_impl: proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - // Returns r{n} as an ident. - fn get_temp(n: usize) -> syn::Ident { - syn::Ident::new(&format!("r{}", n), proc_macro2::Span::call_site()) - } - - // The parameter list for the mont_reduce() internal method. - // r0: u64, mut r1: u64, mut r2: u64, ... - let mut mont_paramlist = proc_macro2::TokenStream::new(); - mont_paramlist.append_separated( - (0..(limbs * 2)).map(|i| (i, get_temp(i))).map(|(i, x)| { - if i != 0 { - quote! {mut #x: u64} - } else { - quote! {#x: u64} - } - }), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - // Implement montgomery reduction for some number of limbs - fn mont_impl(limbs: usize) -> proc_macro2::TokenStream { - let mut gen = proc_macro2::TokenStream::new(); - - for i in 0..limbs { - { - let temp = get_temp(i); - gen.extend(quote! { - let k = #temp.wrapping_mul(INV); - let mut carry = 0; - ::ff::mac_with_carry(#temp, k, MODULUS_LIMBS.0[0], &mut carry); - }); - } - - for j in 1..limbs { - let temp = get_temp(i + j); - gen.extend(quote! { - #temp = ::ff::mac_with_carry(#temp, k, MODULUS_LIMBS.0[#j], &mut carry); - }); - } - - let temp = get_temp(i + limbs); - - if i == 0 { - gen.extend(quote! { - #temp = ::ff::adc(#temp, 0, &mut carry); - }); - } else { - gen.extend(quote! { - #temp = ::ff::adc(#temp, carry2, &mut carry); - }); - } - - if i != (limbs - 1) { - gen.extend(quote! { - let carry2 = carry; - }); - } - } - - for i in 0..limbs { - let temp = get_temp(limbs + i); - - gen.extend(quote! { - self.0[#i] = #temp; - }); - } - - gen - } - - fn sqr_impl(a: proc_macro2::TokenStream, limbs: usize) -> proc_macro2::TokenStream { - let mut gen = proc_macro2::TokenStream::new(); - - for i in 0..(limbs - 1) { - gen.extend(quote! { - let mut carry = 0; - }); - - for j in (i + 1)..limbs { - let temp = get_temp(i + j); - if i == 0 { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, #a.0[#i], #a.0[#j], &mut carry); - }); - } else { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(#temp, #a.0[#i], #a.0[#j], &mut carry); - }); - } - } - - let temp = get_temp(i + limbs); - - gen.extend(quote! { - let #temp = carry; - }); - } - - for i in 1..(limbs * 2) { - let temp0 = get_temp(limbs * 2 - i); - let temp1 = get_temp(limbs * 2 - i - 1); - - if i == 1 { - gen.extend(quote! { - let #temp0 = #temp1 >> 63; - }); - } else if i == (limbs * 2 - 1) { - gen.extend(quote! { - let #temp0 = #temp0 << 1; - }); - } else { - gen.extend(quote! { - let #temp0 = (#temp0 << 1) | (#temp1 >> 63); - }); - } - } - - gen.extend(quote! { - let mut carry = 0; - }); - - for i in 0..limbs { - let temp0 = get_temp(i * 2); - let temp1 = get_temp(i * 2 + 1); - if i == 0 { - gen.extend(quote! { - let #temp0 = ::ff::mac_with_carry(0, #a.0[#i], #a.0[#i], &mut carry); - }); - } else { - gen.extend(quote! { - let #temp0 = ::ff::mac_with_carry(#temp0, #a.0[#i], #a.0[#i], &mut carry); - }); - } - - gen.extend(quote! { - let #temp1 = ::ff::adc(#temp1, 0, &mut carry); - }); - } - - let mut mont_calling = proc_macro2::TokenStream::new(); - mont_calling.append_separated( - (0..(limbs * 2)).map(get_temp), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - gen.extend(quote! { - let mut ret = *self; - ret.mont_reduce(#mont_calling); - ret - }); - - gen - } - - fn mul_impl( - a: proc_macro2::TokenStream, - b: proc_macro2::TokenStream, - limbs: usize, - ) -> proc_macro2::TokenStream { - let mut gen = proc_macro2::TokenStream::new(); - - for i in 0..limbs { - gen.extend(quote! { - let mut carry = 0; - }); - - for j in 0..limbs { - let temp = get_temp(i + j); - - if i == 0 { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(0, #a.0[#i], #b.0[#j], &mut carry); - }); - } else { - gen.extend(quote! { - let #temp = ::ff::mac_with_carry(#temp, #a.0[#i], #b.0[#j], &mut carry); - }); - } - } - - let temp = get_temp(i + limbs); - - gen.extend(quote! { - let #temp = carry; - }); - } - - let mut mont_calling = proc_macro2::TokenStream::new(); - mont_calling.append_separated( - (0..(limbs * 2)).map(get_temp), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - - gen.extend(quote! { - self.mont_reduce(#mont_calling); - }); - - gen - } - - /// Generates an implementation of multiplicative inversion within the target prime - /// field. - fn inv_impl( - a: proc_macro2::TokenStream, - name: &syn::Ident, - modulus: &BigUint, - ) -> proc_macro2::TokenStream { - // Addition chain for p - 2 - let mod_minus_2 = pow_fixed::generate(&a, modulus - BigUint::from(2u64)); - - quote! { - use ::subtle::ConstantTimeEq; - - // By Euler's theorem, if `a` is coprime to `p` (i.e. `gcd(a, p) = 1`), then: - // a^-1 ≡ a^(phi(p) - 1) mod p - // - // `ff_derive` requires that `p` is prime; in this case, `phi(p) = p - 1`, and - // thus: - // a^-1 ≡ a^(p - 2) mod p - let inv = { - #mod_minus_2 - }; - - ::subtle::CtOption::new(inv, !#a.ct_eq(&#name::zero())) - } - } - - let squaring_impl = sqr_impl(quote! {self}, limbs); - let multiply_impl = mul_impl(quote! {self}, quote! {other}, limbs); - let invert_impl = inv_impl(quote! {self}, name, modulus); - let montgomery_impl = mont_impl(limbs); - - // self.0[0].ct_eq(&other.0[0]) & self.0[1].ct_eq(&other.0[1]) & ... - let mut ct_eq_impl = proc_macro2::TokenStream::new(); - ct_eq_impl.append_separated( - (0..limbs).map(|i| quote! { self.0[#i].ct_eq(&other.0[#i]) }), - proc_macro2::Punct::new('&', proc_macro2::Spacing::Alone), - ); - - fn mont_reduce_params(a: proc_macro2::TokenStream, limbs: usize) -> proc_macro2::TokenStream { - // a.0[0], a.0[1], ..., 0, 0, 0, 0, ... - let mut mont_reduce_params = proc_macro2::TokenStream::new(); - mont_reduce_params.append_separated( - (0..limbs) - .map(|i| quote! { #a.0[#i] }) - .chain((0..limbs).map(|_| quote! {0})), - proc_macro2::Punct::new(',', proc_macro2::Spacing::Alone), - ); - mont_reduce_params - } - - let mont_reduce_self_params = mont_reduce_params(quote! {self}, limbs); - let mont_reduce_other_params = mont_reduce_params(quote! {other}, limbs); - - let repr_endianness = endianness.repr_endianness(); - let from_repr_impl = endianness.from_repr(name, limbs); - let to_repr_impl = endianness.to_repr(repr, &mont_reduce_self_params, limbs); - - let top_limb_index = limbs - 1; - - quote! { - impl ::core::marker::Copy for #name { } - - impl ::core::clone::Clone for #name { - fn clone(&self) -> #name { - *self - } - } - - impl ::core::default::Default for #name { - fn default() -> #name { - #name::zero() - } - } - - impl ::subtle::ConstantTimeEq for #name { - fn ct_eq(&self, other: &#name) -> ::subtle::Choice { - self.to_repr().ct_eq(&other.to_repr()) - } - } - - impl ::core::cmp::PartialEq for #name { - fn eq(&self, other: &#name) -> bool { - use ::subtle::ConstantTimeEq; - self.ct_eq(other).into() - } - } - - impl ::core::cmp::Eq for #name { } - - impl ::core::fmt::Debug for #name - { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({:?})", stringify!(#name), self.to_repr()) - } - } - - /// Elements are ordered lexicographically. - impl Ord for #name { - #[inline(always)] - fn cmp(&self, other: &#name) -> ::core::cmp::Ordering { - let mut a = *self; - a.mont_reduce( - #mont_reduce_self_params - ); - - let mut b = *other; - b.mont_reduce( - #mont_reduce_other_params - ); - - a.cmp_native(&b) - } - } - - impl PartialOrd for #name { - #[inline(always)] - fn partial_cmp(&self, other: &#name) -> Option<::core::cmp::Ordering> { - Some(self.cmp(other)) - } - } - - impl ::core::fmt::Display for #name { - fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { - write!(f, "{}({})", stringify!(#name), self.to_repr()) - } - } - - impl From for #name { - #[inline(always)] - fn from(val: u64) -> #name { - let mut raw = [0u64; #limbs]; - raw[0] = val; - #name(raw) * R2 - } - } - - impl From<#name> for #repr { - fn from(e: #name) -> #repr { - e.to_repr() - } - } - - impl<'a> From<&'a #name> for #repr { - fn from(e: &'a #name) -> #repr { - e.to_repr() - } - } - - impl ::subtle::ConditionallySelectable for #name { - fn conditional_select(a: &#name, b: &#name, choice: ::subtle::Choice) -> #name { - let mut res = [0u64; #limbs]; - for i in 0..#limbs { - res[i] = u64::conditional_select(&a.0[i], &b.0[i], choice); - } - #name(res) - } - } - - impl ::core::ops::Neg for #name { - type Output = #name; - - #[inline] - fn neg(self) -> #name { - let mut ret = self; - if !ret.is_zero() { - let mut tmp = MODULUS_LIMBS; - tmp.sub_noborrow(&ret); - ret = tmp; - } - ret - } - } - - impl<'r> ::core::ops::Add<&'r #name> for #name { - type Output = #name; - - #[inline] - fn add(self, other: &#name) -> #name { - let mut ret = self; - ret.add_assign(other); - ret - } - } - - impl ::core::ops::Add for #name { - type Output = #name; - - #[inline] - fn add(self, other: #name) -> Self { - self + &other - } - } - - impl<'r> ::core::ops::AddAssign<&'r #name> for #name { - #[inline] - fn add_assign(&mut self, other: &#name) { - // This cannot exceed the backing capacity. - self.add_nocarry(other); - - // However, it may need to be reduced. - self.reduce(); - } - } - - impl ::core::ops::AddAssign for #name { - #[inline] - fn add_assign(&mut self, other: #name) { - self.add_assign(&other); - } - } - - impl<'r> ::core::ops::Sub<&'r #name> for #name { - type Output = #name; - - #[inline] - fn sub(self, other: &#name) -> Self { - let mut ret = self; - ret.sub_assign(other); - ret - } - } - - impl ::core::ops::Sub for #name { - type Output = #name; - - #[inline] - fn sub(self, other: #name) -> Self { - self - &other - } - } - - impl<'r> ::core::ops::SubAssign<&'r #name> for #name { - #[inline] - fn sub_assign(&mut self, other: &#name) { - // If `other` is larger than `self`, we'll need to add the modulus to self first. - if other.cmp_native(self) == ::core::cmp::Ordering::Greater { - self.add_nocarry(&MODULUS_LIMBS); - } - - self.sub_noborrow(other); - } - } - - impl ::core::ops::SubAssign for #name { - #[inline] - fn sub_assign(&mut self, other: #name) { - self.sub_assign(&other); - } - } - - impl<'r> ::core::ops::Mul<&'r #name> for #name { - type Output = #name; - - #[inline] - fn mul(self, other: &#name) -> Self { - let mut ret = self; - ret.mul_assign(other); - ret - } - } - - impl ::core::ops::Mul for #name { - type Output = #name; - - #[inline] - fn mul(self, other: #name) -> Self { - self * &other - } - } - - impl<'r> ::core::ops::MulAssign<&'r #name> for #name { - #[inline] - fn mul_assign(&mut self, other: &#name) - { - #multiply_impl - } - } - - impl ::core::ops::MulAssign for #name { - #[inline] - fn mul_assign(&mut self, other: #name) - { - self.mul_assign(&other); - } - } - - impl ::ff::PrimeField for #name { - type Repr = #repr; - type ReprEndianness = #repr_endianness; - - fn from_repr(r: #repr) -> Option<#name> { - #from_repr_impl - } - - fn to_repr(&self) -> #repr { - #to_repr_impl - } - - #[inline(always)] - fn is_odd(&self) -> bool { - let mut r = *self; - r.mont_reduce( - #mont_reduce_self_params - ); - - r.0[0] & 1 == 1 - } - - fn char() -> Self::Repr { - MODULUS - } - - const NUM_BITS: u32 = MODULUS_BITS; - - const CAPACITY: u32 = Self::NUM_BITS - 1; - - fn multiplicative_generator() -> Self { - GENERATOR - } - - const S: u32 = S; - - fn root_of_unity() -> Self { - ROOT_OF_UNITY - } - } - - impl ::ff::Field for #name { - /// Computes a uniformly random element using rejection sampling. - fn random(rng: &mut R) -> Self { - loop { - let mut tmp = { - let mut repr = [0u64; #limbs]; - for i in 0..#limbs { - repr[i] = rng.next_u64(); - } - #name(repr) - }; - - // Mask away the unused most-significant bits. - tmp.0.as_mut()[#top_limb_index] &= 0xffffffffffffffff >> REPR_SHAVE_BITS; - - if tmp.is_valid() { - return tmp - } - } - } - - #[inline] - fn zero() -> Self { - #name([0; #limbs]) - } - - #[inline] - fn one() -> Self { - R - } - - #[inline] - fn is_zero(&self) -> bool { - self.0.iter().all(|&e| e == 0) - } - - #[inline] - fn double(&self) -> Self { - let mut ret = *self; - - // This cannot exceed the backing capacity. - let mut last = 0; - for i in &mut ret.0 { - let tmp = *i >> 63; - *i <<= 1; - *i |= last; - last = tmp; - } - - // However, it may need to be reduced. - ret.reduce(); - - ret - } - - fn invert(&self) -> ::subtle::CtOption { - #invert_impl - } - - #[inline] - fn square(&self) -> Self - { - #squaring_impl - } - - fn sqrt(&self) -> ::subtle::CtOption { - #sqrt_impl - } - } - - impl #name { - /// Compares two elements in native representation. This is only used - /// internally. - #[inline(always)] - fn cmp_native(&self, other: &#name) -> ::core::cmp::Ordering { - for (a, b) in self.0.iter().rev().zip(other.0.iter().rev()) { - if a < b { - return ::core::cmp::Ordering::Less - } else if a > b { - return ::core::cmp::Ordering::Greater - } - } - - ::core::cmp::Ordering::Equal - } - - /// Determines if the element is really in the field. This is only used - /// internally. - #[inline(always)] - fn is_valid(&self) -> bool { - // The Ord impl calls `reduce`, which in turn calls `is_valid`, so we use - // this internal function to eliminate the cycle. - self.cmp_native(&MODULUS_LIMBS) == ::core::cmp::Ordering::Less - } - - #[inline(always)] - fn add_nocarry(&mut self, other: &#name) { - let mut carry = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::adc(*a, *b, &mut carry); - } - } - - #[inline(always)] - fn sub_noborrow(&mut self, other: &#name) { - let mut borrow = 0; - - for (a, b) in self.0.iter_mut().zip(other.0.iter()) { - *a = ::ff::sbb(*a, *b, &mut borrow); - } - } - - /// Subtracts the modulus from this element if this element is not in the - /// field. Only used interally. - #[inline(always)] - fn reduce(&mut self) { - if !self.is_valid() { - self.sub_noborrow(&MODULUS_LIMBS); - } - } - - #[inline(always)] - fn mont_reduce( - &mut self, - #mont_paramlist - ) - { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - #montgomery_impl - - self.reduce(); - } - } - } -} diff --git a/ff/ff_derive/src/pow_fixed.rs b/ff/ff_derive/src/pow_fixed.rs deleted file mode 100644 index 1d2b37ad2..000000000 --- a/ff/ff_derive/src/pow_fixed.rs +++ /dev/null @@ -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 -} diff --git a/ff/src/lib.rs b/ff/src/lib.rs deleted file mode 100644 index 9d96f5ede..000000000 --- a/ff/src/lib.rs +++ /dev/null @@ -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 - + Sub - + Mul - + Neg - + 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(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; - - /// Returns the square root of the field element, if it is - /// quadratic residue. - fn sqrt(&self) -> CtOption; - - /// 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>(&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: &mut T); -} - -impl Endianness for byteorder::BigEndian { - fn toggle_little_endian>(t: &mut T) { - t.as_mut().reverse(); - } -} - -impl Endianness for byteorder::LittleEndian { - fn toggle_little_endian>(_: &mut T) { - // No-op - } -} - -/// This represents an element of a prime field. -pub trait PrimeField: Field + From { - /// The prime field can be converted back and forth into this binary - /// representation. - type Repr: Default + AsRef<[u8]> + AsMut<[u8]> + From + 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 { - 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; - - /// 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, - n: usize, - _limb: PhantomData, -} - -impl> BitIterator { - pub fn new(t: E) -> Self { - let n = t.as_ref().len() * 64; - - BitIterator { - t, - n, - _limb: PhantomData::default(), - } - } -} - -impl> Iterator for BitIterator { - type Item = bool; - - fn next(&mut self) -> Option { - 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> BitIterator { - pub fn new(t: E) -> Self { - let n = t.as_ref().len() * 8; - - BitIterator { - t, - n, - _limb: PhantomData::default(), - } - } -} - -impl> Iterator for BitIterator { - type Item = bool; - - fn next(&mut self) -> Option { - 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::::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::::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 - } -} diff --git a/group/.gitignore b/group/.gitignore deleted file mode 100644 index 693699042..000000000 --- a/group/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock diff --git a/group/COPYRIGHT b/group/COPYRIGHT deleted file mode 100644 index 849e32747..000000000 --- a/group/COPYRIGHT +++ /dev/null @@ -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. diff --git a/group/Cargo.toml b/group/Cargo.toml deleted file mode 100644 index 57ad76f81..000000000 --- a/group/Cargo.toml +++ /dev/null @@ -1,25 +0,0 @@ -[package] -name = "group" -version = "0.7.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -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" } diff --git a/group/LICENSE-APACHE b/group/LICENSE-APACHE deleted file mode 100644 index 16fe87b06..000000000 --- a/group/LICENSE-APACHE +++ /dev/null @@ -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. diff --git a/group/LICENSE-MIT b/group/LICENSE-MIT deleted file mode 100644 index 31aa79387..000000000 --- a/group/LICENSE-MIT +++ /dev/null @@ -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. diff --git a/group/README.md b/group/README.md deleted file mode 100644 index 5c2398b2b..000000000 --- a/group/README.md +++ /dev/null @@ -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. diff --git a/group/src/cofactor.rs b/group/src/cofactor.rs deleted file mode 100644 index 472290935..000000000 --- a/group/src/cofactor.rs +++ /dev/null @@ -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<::Subgroup> - + GroupOpsOwned<::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 + Into; - - /// 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; - - /// 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(); - ::ReprEndianness::toggle_little_endian(&mut char); - - // Multiply self by the characteristic to eliminate any prime-order subgroup - // component. - let bits = BitIterator::::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::Affine> + CofactorGroup -{ - type Affine: CofactorCurveAffine - + Mul - + for<'r> Mul; -} - -/// 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 - + Mul<::Scalar, Output = ::Curve> - + for<'r> Mul< - ::Scalar, - Output = ::Curve, - > -{ - type Scalar: PrimeField; - type Curve: CofactorCurve; - - /// 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; -} diff --git a/group/src/lib.rs b/group/src/lib.rs deleted file mode 100644 index cae3b81cf..000000000 --- a/group/src/lib.rs +++ /dev/null @@ -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: - Add + Sub + AddAssign + SubAssign -{ -} - -impl GroupOps for T where - T: Add + Sub + AddAssign + SubAssign -{ -} - -/// A helper trait for references with a group operation. -pub trait GroupOpsOwned: for<'r> GroupOps<&'r Rhs, Output> {} -impl GroupOpsOwned for T where T: for<'r> GroupOps<&'r Rhs, Output> {} - -/// A helper trait for types implementing group scalar multiplication. -pub trait ScalarMul: Mul + MulAssign {} - -impl ScalarMul for T where T: Mul + MulAssign -{} - -/// A helper trait for references implementing group scalar multiplication. -pub trait ScalarMulOwned: for<'r> ScalarMul<&'r Rhs, Output> {} -impl ScalarMulOwned 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 - + GroupOps - + GroupOpsOwned - + ScalarMul<::Scalar> - + ScalarMulOwned<::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(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<::AffineRepr> + GroupOpsOwned<::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 { unimplemented!() } - /// # fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { unimplemented!() } - /// # fn to_bytes(&self) -> Self::Repr { unimplemented!() } - /// # } - /// # let buf = &[0u8; 0][..]; - /// let mut encoding = ::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; - - /// 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; - - /// 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; - - /// 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; - - /// Converts this element into its uncompressed encoding, so long as it's not - /// the point at infinity. - fn to_uncompressed(&self) -> Self::Uncompressed; -} diff --git a/group/src/prime.rs b/group/src/prime.rs deleted file mode 100644 index d02105d79..000000000 --- a/group/src/prime.rs +++ /dev/null @@ -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::Affine> + PrimeGroup { - type Affine: PrimeCurveAffine - + Mul - + for<'r> Mul; -} - -/// 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 - + Mul<::Scalar, Output = ::Curve> - + for<'r> Mul<::Scalar, Output = ::Curve> -{ - type Scalar: PrimeField; - type Curve: PrimeCurve; - - /// 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; -} diff --git a/group/src/tests/mod.rs b/group/src/tests/mod.rs deleted file mode 100644 index 0be5e56af..000000000 --- a/group/src/tests/mod.rs +++ /dev/null @@ -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() { - 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::(); - random_multiplication_tests::(); - random_doubling_tests::(); - random_negation_tests::(); - random_transformation_tests::(); - random_compressed_encoding_tests::(); -} - -pub fn random_wnaf_tests() { - 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) {} - - 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() { - 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() { - 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() { - 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::::mul(a_affine, s); - tmp3.add_assign(Mul::::mul(b_affine, s)); - - assert_eq!(tmp1, tmp2); - assert_eq!(tmp1, tmp3); - } -} - -fn random_addition_tests() { - 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() { - 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::>(); - - 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::>(); - - 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() { - 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() -where - ::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); - } -} diff --git a/group/src/wnaf.rs b/group/src/wnaf.rs deleted file mode 100644 index e81adea2d..000000000 --- a/group/src/wnaf.rs +++ /dev/null @@ -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(table: &mut Vec, 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>(wnaf: &mut Vec, 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(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 { - base: B, - scalar: S, - window_size: W, -} - -impl Wnaf<(), Vec, Vec> { - /// Construct a new wNAF context without allocating. - pub fn new() -> Self { - Wnaf { - base: vec![], - scalar: vec![], - window_size: (), - } - } -} - -impl Wnaf<(), Vec, Vec> { - /// 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> { - // 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: &::Scalar) -> Wnaf, &[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> { - /// 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> { - Wnaf { - base: self.base, - scalar: vec![], - window_size: self.window_size, - } - } -} - -impl<'a, G: Group> Wnaf, &'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, &'a [i64]> { - Wnaf { - base: vec![], - scalar: self.scalar, - window_size: self.window_size, - } - } -} - -impl> Wnaf { - /// Performs exponentiation given a base. - pub fn base(&mut self, base: G) -> G - where - B: AsMut>, - { - wnaf_table(self.base.as_mut(), base, self.window_size); - wnaf_exp(self.base.as_mut(), self.scalar.as_ref()) - } -} - -impl>> Wnaf { - /// Performs exponentiation given a scalar. - pub fn scalar(&mut self, scalar: &::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()) - } -} diff --git a/jubjub/.github/workflows/ci.yml b/jubjub/.github/workflows/ci.yml deleted file mode 100644 index 5d0efb3a2..000000000 --- a/jubjub/.github/workflows/ci.yml +++ /dev/null @@ -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 diff --git a/jubjub/.gitignore b/jubjub/.gitignore deleted file mode 100644 index 693699042..000000000 --- a/jubjub/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/target -**/*.rs.bk -Cargo.lock diff --git a/jubjub/COPYRIGHT b/jubjub/COPYRIGHT deleted file mode 100644 index aaca1cc67..000000000 --- a/jubjub/COPYRIGHT +++ /dev/null @@ -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. diff --git a/jubjub/Cargo.toml b/jubjub/Cargo.toml deleted file mode 100644 index 3f9485ca9..000000000 --- a/jubjub/Cargo.toml +++ /dev/null @@ -1,63 +0,0 @@ -[package] -authors = [ - "Sean Bowe ", - "Eirik Ogilvie-Wigley ", - "Jack Grigg ", -] -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 diff --git a/jubjub/LICENSE-APACHE b/jubjub/LICENSE-APACHE deleted file mode 100644 index 16fe87b06..000000000 --- a/jubjub/LICENSE-APACHE +++ /dev/null @@ -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. diff --git a/jubjub/LICENSE-MIT b/jubjub/LICENSE-MIT deleted file mode 100644 index 31aa79387..000000000 --- a/jubjub/LICENSE-MIT +++ /dev/null @@ -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. diff --git a/jubjub/README.md b/jubjub/README.md deleted file mode 100644 index da5bd530a..000000000 --- a/jubjub/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# jubjub [![Crates.io](https://img.shields.io/crates/v/jubjub.svg)](https://crates.io/crates/jubjub) # - - - -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. diff --git a/jubjub/RELEASES.md b/jubjub/RELEASES.md deleted file mode 100644 index b9a1e9eff..000000000 --- a/jubjub/RELEASES.md +++ /dev/null @@ -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` 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. diff --git a/jubjub/benches/fq_bench.rs b/jubjub/benches/fq_bench.rs deleted file mode 100644 index 65eceafc8..000000000 --- a/jubjub/benches/fq_bench.rs +++ /dev/null @@ -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); diff --git a/jubjub/benches/fr_bench.rs b/jubjub/benches/fr_bench.rs deleted file mode 100644 index 8dc9ce221..000000000 --- a/jubjub/benches/fr_bench.rs +++ /dev/null @@ -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); diff --git a/jubjub/benches/point_bench.rs b/jubjub/benches/point_bench.rs deleted file mode 100644 index 1659ea5f9..000000000 --- a/jubjub/benches/point_bench.rs +++ /dev/null @@ -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); diff --git a/jubjub/doc/derive/.gitignore b/jubjub/doc/derive/.gitignore deleted file mode 100644 index 7c974cf29..000000000 --- a/jubjub/doc/derive/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.sage.py diff --git a/jubjub/doc/derive/derive.sage b/jubjub/doc/derive/derive.sage deleted file mode 100644 index c0c5310bf..000000000 --- a/jubjub/doc/derive/derive.sage +++ /dev/null @@ -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) diff --git a/jubjub/doc/evidence/.gitignore b/jubjub/doc/evidence/.gitignore deleted file mode 100644 index 9a0d287f2..000000000 --- a/jubjub/doc/evidence/.gitignore +++ /dev/null @@ -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/ - diff --git a/jubjub/doc/evidence/LICENSE b/jubjub/doc/evidence/LICENSE deleted file mode 100644 index 9e181634a..000000000 --- a/jubjub/doc/evidence/LICENSE +++ /dev/null @@ -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. diff --git a/jubjub/doc/evidence/README.md b/jubjub/doc/evidence/README.md deleted file mode 100644 index 26b2e36f2..000000000 --- a/jubjub/doc/evidence/README.md +++ /dev/null @@ -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. diff --git a/jubjub/doc/evidence/a b/jubjub/doc/evidence/a deleted file mode 100644 index 3a2e3f498..000000000 --- a/jubjub/doc/evidence/a +++ /dev/null @@ -1 +0,0 @@ --1 diff --git a/jubjub/doc/evidence/d b/jubjub/doc/evidence/d deleted file mode 100644 index 767309a72..000000000 --- a/jubjub/doc/evidence/d +++ /dev/null @@ -1 +0,0 @@ -19257038036680949359750312669786877991949435402254120286184196891950884077233 diff --git a/jubjub/doc/evidence/l b/jubjub/doc/evidence/l deleted file mode 100644 index 83f92d579..000000000 --- a/jubjub/doc/evidence/l +++ /dev/null @@ -1 +0,0 @@ -6554484396890773809930967563523245729705921265872317281365359162392183254199 diff --git a/jubjub/doc/evidence/p b/jubjub/doc/evidence/p deleted file mode 100644 index 1dc0557a7..000000000 --- a/jubjub/doc/evidence/p +++ /dev/null @@ -1 +0,0 @@ -52435875175126190479447740508185965837690552500527637822603658699938581184513 diff --git a/jubjub/doc/evidence/rigid b/jubjub/doc/evidence/rigid deleted file mode 100644 index e560e4087..000000000 --- a/jubjub/doc/evidence/rigid +++ /dev/null @@ -1 +0,0 @@ -fully rigid diff --git a/jubjub/doc/evidence/run.sh b/jubjub/doc/evidence/run.sh deleted file mode 100644 index 817f2faf8..000000000 --- a/jubjub/doc/evidence/run.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/sh -sage verify.sage . -grep -Rn '.' verify-* |grep '^verify-.*:1:' |sed 's/:1:/ = /' - diff --git a/jubjub/doc/evidence/shape b/jubjub/doc/evidence/shape deleted file mode 100644 index 796f74d09..000000000 --- a/jubjub/doc/evidence/shape +++ /dev/null @@ -1 +0,0 @@ -tedwards diff --git a/jubjub/doc/evidence/verify.sage b/jubjub/doc/evidence/verify.sage deleted file mode 100644 index 1717c0bc6..000000000 --- a/jubjub/doc/evidence/verify.sage +++ /dev/null @@ -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', '\n' + - ''.join(('2\n' if v == 2 else - '%s\n' % (v,v)) for v in V) + - '\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 += '

Take b = %s.\n' % base - proof += '

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 += '

2 is prime.\n' - else: - proof += '

%s is prime.\n' % (v,v) - proof += '
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 += '

(%s) divides n-1.\n' % f - proof += '

(%s)^2 > n.\n' % f - proof += "

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. = 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','
= %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','%s
= (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','%s
= %s
≈ -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','
= %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',"%s
= (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%+dx^2+x' % (B,A) - if B == 1: - equation = 'y^2 = x^3%+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%+dx%+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() - diff --git a/jubjub/doc/evidence/x0 b/jubjub/doc/evidence/x0 deleted file mode 100644 index 3b2097a36..000000000 --- a/jubjub/doc/evidence/x0 +++ /dev/null @@ -1 +0,0 @@ -11076627216317271660298050606127911965867021807910416450833192264015104452986 diff --git a/jubjub/doc/evidence/x1 b/jubjub/doc/evidence/x1 deleted file mode 100644 index c8c8fc308..000000000 --- a/jubjub/doc/evidence/x1 +++ /dev/null @@ -1 +0,0 @@ -8076246640662884909881801758704306714034609987455869804520522091855516602923 diff --git a/jubjub/doc/evidence/y0 b/jubjub/doc/evidence/y0 deleted file mode 100644 index b47cd2764..000000000 --- a/jubjub/doc/evidence/y0 +++ /dev/null @@ -1 +0,0 @@ -44412834903739585386157632289020980010620626017712148233229312325549216099227 diff --git a/jubjub/doc/evidence/y1 b/jubjub/doc/evidence/y1 deleted file mode 100644 index a46479fb5..000000000 --- a/jubjub/doc/evidence/y1 +++ /dev/null @@ -1 +0,0 @@ -13262374693698910701929044844600465831413122818447359594527400194675274060458 diff --git a/jubjub/src/fr.rs b/jubjub/src/fr.rs deleted file mode 100644 index 139855847..000000000 --- a/jubjub/src/fr.rs +++ /dev/null @@ -1,1132 +0,0 @@ -//! This module provides an implementation of the Jubjub scalar field $\mathbb{F}_r$ -//! where `r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` - -use core::convert::TryInto; -use core::fmt; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; - -use ff::{Field, PrimeField}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -use crate::util::{adc, mac, sbb}; - -/// Represents an element of the scalar field $\mathbb{F}_r$ of the Jubjub elliptic -/// curve construction. -// The internal representation of this type is four 64-bit unsigned -// integers in little-endian order. Elements of Fr are always in -// Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. -#[derive(Clone, Copy, Eq)] -pub struct Fr(pub(crate) [u64; 4]); - -impl fmt::Debug for Fr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let tmp = self.to_bytes(); - write!(f, "0x")?; - for &b in tmp.iter().rev() { - write!(f, "{:02x}", b)?; - } - Ok(()) - } -} - -impl fmt::Display for Fr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl From for Fr { - fn from(val: u64) -> Fr { - Fr([val, 0, 0, 0]) * R2 - } -} - -impl ConstantTimeEq for Fr { - 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]) - } -} - -impl PartialEq for Fr { - #[inline] - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl ConditionallySelectable for Fr { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - Fr([ - 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), - ]) - } -} - -/// Constant representing the modulus -/// r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 -pub const MODULUS: Fr = Fr([ - 0xd097_0e5e_d6f7_2cb7, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, -]); - -const MODULUS_BYTES: [u8; 32] = [ - 0xb7, 0x2c, 0xf7, 0xd6, 0x5e, 0x0e, 0x97, 0xd0, 0x82, 0x10, 0xc8, 0xcc, 0x93, 0x20, 0x68, 0xa6, - 0x00, 0x3b, 0x34, 0x01, 0x01, 0x3b, 0x67, 0x06, 0xa9, 0xaf, 0x33, 0x65, 0xea, 0xb4, 0x7d, 0x0e, -]; - -// The number of bits needed to represent the modulus. -const MODULUS_BITS: u32 = 252; - -// GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) -const GENERATOR: Fr = Fr([ - 0x720b_1b19_d49e_a8f1, - 0xbf4a_a361_01f1_3a58, - 0x5fa8_cc96_8193_ccbb, - 0x0e70_cbdc_7dcc_f3ac, -]); - -// 2^S * t = MODULUS - 1 with t odd -const S: u32 = 1; - -// 2^S root of unity computed by GENERATOR^t -const ROOT_OF_UNITY: Fr = Fr([ - 0xaa9f_02ab_1d61_24de, - 0xb352_4a64_6611_2932, - 0x7342_2612_15ac_260b, - 0x04d6_b87b_1da2_59e2, -]); - -impl<'a> Neg for &'a Fr { - type Output = Fr; - - #[inline] - fn neg(self) -> Fr { - self.neg() - } -} - -impl Neg for Fr { - type Output = Fr; - - #[inline] - fn neg(self) -> Fr { - -&self - } -} - -impl<'a, 'b> Sub<&'b Fr> for &'a Fr { - type Output = Fr; - - #[inline] - fn sub(self, rhs: &'b Fr) -> Fr { - self.sub(rhs) - } -} - -impl<'a, 'b> Add<&'b Fr> for &'a Fr { - type Output = Fr; - - #[inline] - fn add(self, rhs: &'b Fr) -> Fr { - self.add(rhs) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a Fr { - type Output = Fr; - - #[inline] - fn mul(self, rhs: &'b Fr) -> Fr { - // Schoolbook multiplication - - self.mul(rhs) - } -} - -impl_binops_additive!(Fr, Fr); -impl_binops_multiplicative!(Fr, Fr); - -/// INV = -(r^{-1} mod 2^64) mod 2^64 -const INV: u64 = 0x1ba3_a358_ef78_8ef9; - -/// R = 2^256 mod r -const R: Fr = Fr([ - 0x25f8_0bb3_b996_07d9, - 0xf315_d62f_66b6_e750, - 0x9325_14ee_eb88_14f4, - 0x09a6_fc6f_4791_55c6, -]); - -/// R^2 = 2^512 mod r -const R2: Fr = Fr([ - 0x6771_9aa4_95e5_7731, - 0x51b0_cef0_9ce3_fc26, - 0x69da_b7fa_c026_e9a5, - 0x04f6_547b_8d12_7688, -]); - -/// R^3 = 2^768 mod r -const R3: Fr = Fr([ - 0xe0d6_c656_3d83_0544, - 0x323e_3883_598d_0f85, - 0xf0fe_a300_4c2e_2ba8, - 0x0587_4f84_9467_37ec, -]); - -impl Default for Fr { - fn default() -> Self { - Self::zero() - } -} - -impl Fr { - /// Returns zero, the additive identity. - #[inline] - pub const fn zero() -> Fr { - Fr([0, 0, 0, 0]) - } - - /// Returns one, the multiplicative identity. - #[inline] - pub const fn one() -> Fr { - R - } - - /// Doubles this field element. - #[inline] - pub const fn double(&self) -> Fr { - self.add(self) - } - - /// Attempts to convert a little-endian byte representation of - /// a field element into an element of `Fr`, failing if the input - /// is not canonical (is not smaller than r). - pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { - let mut tmp = Fr([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); - tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); - tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); - tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], 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 `Fr` into a byte representation in - /// little-endian byte order. - pub fn to_bytes(&self) -> [u8; 32] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Fr::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); - - let mut res = [0; 32]; - res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); - - res - } - - /// Converts a 512-bit little endian integer into - /// an element of Fr by reducing modulo r. - pub fn from_bytes_wide(bytes: &[u8; 64]) -> Fr { - Fr::from_u512([ - u64::from_le_bytes(bytes[0..8].try_into().unwrap()), - u64::from_le_bytes(bytes[8..16].try_into().unwrap()), - u64::from_le_bytes(bytes[16..24].try_into().unwrap()), - u64::from_le_bytes(bytes[24..32].try_into().unwrap()), - u64::from_le_bytes(bytes[32..40].try_into().unwrap()), - u64::from_le_bytes(bytes[40..48].try_into().unwrap()), - u64::from_le_bytes(bytes[48..56].try_into().unwrap()), - u64::from_le_bytes(bytes[56..64].try_into().unwrap()), - ]) - } - - fn from_u512(limbs: [u64; 8]) -> Fr { - // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits - // with the higher bits multiplied by 2^256. 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^256 = R^3 - // - // and computing their sum in the field. It remains to see that arbitrary 256-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^256 multiplied by - // the modulus. This holds because for any `c` smaller than the modulus, we have - // that (2^256 - 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 d0 = Fr([limbs[0], limbs[1], limbs[2], limbs[3]]); - let d1 = Fr([limbs[4], limbs[5], limbs[6], limbs[7]]); - // Convert to Montgomery form - d0 * R2 + d1 * R3 - } - - /// Converts from an integer represented in little endian - /// into its (congruent) `Fr` representation. - pub const fn from_raw(val: [u64; 4]) -> Self { - (&Fr(val)).mul(&R2) - } - - /// Squares this element. - #[inline] - pub const fn square(&self) -> Fr { - let (r1, carry) = mac(0, self.0[0], self.0[1], 0); - let (r2, carry) = mac(0, self.0[0], self.0[2], carry); - let (r3, r4) = mac(0, self.0[0], self.0[3], carry); - - let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); - let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); - - let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); - - let r7 = r6 >> 63; - let r6 = (r6 << 1) | (r5 >> 63); - let r5 = (r5 << 1) | (r4 >> 63); - let r4 = (r4 << 1) | (r3 >> 63); - let r3 = (r3 << 1) | (r2 >> 63); - let r2 = (r2 << 1) | (r1 >> 63); - let r1 = r1 << 1; - - let (r0, carry) = mac(0, self.0[0], self.0[0], 0); - let (r1, carry) = adc(0, r1, carry); - let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); - let (r3, carry) = adc(0, r3, carry); - let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); - let (r5, carry) = adc(0, r5, carry); - let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); - let (r7, _) = adc(0, r7, carry); - - Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Computes the square root of this element, if it exists. - pub fn sqrt(&self) -> CtOption { - // Because r = 3 (mod 4) - // sqrt can be done with only one exponentiation, - // via the computation of self^((r + 1) // 4) (mod r) - let sqrt = self.pow_vartime(&[ - 0xb425_c397_b5bd_cb2e, - 0x299a_0824_f332_0420, - 0x4199_cec0_404d_0ec0, - 0x039f_6d3a_994c_ebea, - ]); - - CtOption::new( - sqrt, - (sqrt * sqrt).ct_eq(self), // Only return Some if it's the square root. - ) - } - - /// Exponentiates `self` by `by`, where `by` is a - /// little-endian order integer exponent. - pub fn pow(&self, by: &[u64; 4]) -> Self { - let mut res = Self::one(); - for e in by.iter().rev() { - for i in (0..64).rev() { - res = res.square(); - let mut tmp = res; - tmp.mul_assign(self); - res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); - } - } - res - } - - /// Exponentiates `self` by `by`, where `by` 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. - pub fn pow_vartime(&self, by: &[u64; 4]) -> 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.mul_assign(self); - } - } - } - res - } - - /// Computes the multiplicative inverse of this element, - /// failing if the element is zero. - pub fn invert(&self) -> CtOption { - #[inline(always)] - fn square_assign_multi(n: &mut Fr, num_times: usize) { - for _ in 0..num_times { - *n = n.square(); - } - } - // found using https://github.com/kwantam/addchain - let mut t1 = self.square(); - let mut t0 = t1.square(); - let mut t3 = t0 * t1; - let t6 = t3 * self; - let t7 = t6 * t1; - let t12 = t7 * t3; - let t13 = t12 * t0; - let t16 = t12 * t3; - let t2 = t13 * t3; - let t15 = t16 * t3; - let t19 = t2 * t0; - let t9 = t15 * t3; - let t18 = t9 * t3; - let t14 = t18 * t1; - let t4 = t18 * t0; - let t8 = t18 * t3; - let t17 = t14 * t3; - let t11 = t8 * t3; - t1 = t17 * t3; - let t5 = t11 * t3; - t3 = t5 * t0; - t0 = t5.square(); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t3); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t8); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t19); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t13); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t14); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t18); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t17); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t16); - square_assign_multi(&mut t0, 3); - t0.mul_assign(self); - square_assign_multi(&mut t0, 11); - t0.mul_assign(&t11); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t5); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t15); - square_assign_multi(&mut t0, 8); - t0.mul_assign(self); - square_assign_multi(&mut t0, 12); - t0.mul_assign(&t13); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t9); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t15); - square_assign_multi(&mut t0, 14); - t0.mul_assign(&t14); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t13); - square_assign_multi(&mut t0, 2); - t0.mul_assign(self); - square_assign_multi(&mut t0, 6); - t0.mul_assign(self); - square_assign_multi(&mut t0, 9); - t0.mul_assign(&t7); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t12); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t11); - square_assign_multi(&mut t0, 3); - t0.mul_assign(self); - square_assign_multi(&mut t0, 12); - t0.mul_assign(&t9); - square_assign_multi(&mut t0, 11); - t0.mul_assign(&t8); - square_assign_multi(&mut t0, 8); - t0.mul_assign(&t7); - square_assign_multi(&mut t0, 4); - t0.mul_assign(&t6); - square_assign_multi(&mut t0, 10); - t0.mul_assign(&t5); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t3); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t4); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t3); - square_assign_multi(&mut t0, 5); - t0.mul_assign(&t2); - square_assign_multi(&mut t0, 6); - t0.mul_assign(&t2); - square_assign_multi(&mut t0, 7); - t0.mul_assign(&t1); - - CtOption::new(t0, !self.ct_eq(&Self::zero())) - } - - #[inline] - #[allow(clippy::too_many_arguments)] - const fn montgomery_reduce( - r0: u64, - r1: u64, - r2: u64, - r3: u64, - r4: u64, - r5: u64, - r6: u64, - r7: u64, - ) -> Self { - // The Montgomery reduction here is based on Algorithm 14.32 in - // Handbook of Applied Cryptography - // . - - let k = r0.wrapping_mul(INV); - let (_, carry) = mac(r0, k, MODULUS.0[0], 0); - let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); - let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); - let (r4, carry2) = adc(r4, 0, carry); - - let k = r1.wrapping_mul(INV); - let (_, carry) = mac(r1, k, MODULUS.0[0], 0); - let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); - let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); - let (r5, carry2) = adc(r5, carry2, carry); - - let k = r2.wrapping_mul(INV); - let (_, carry) = mac(r2, k, MODULUS.0[0], 0); - let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); - let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); - let (r6, carry2) = adc(r6, carry2, carry); - - let k = r3.wrapping_mul(INV); - let (_, carry) = mac(r3, k, MODULUS.0[0], 0); - let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); - let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); - let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); - let (r7, _) = adc(r7, carry2, carry); - - // Result may be within MODULUS of the correct value - (&Fr([r4, r5, r6, r7])).sub(&MODULUS) - } - - /// Multiplies this element by another element - #[inline] - pub const fn mul(&self, rhs: &Self) -> Self { - // Schoolbook multiplication - - let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); - let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); - let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); - let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); - - let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); - let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); - let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); - let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); - - let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); - let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); - let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); - let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); - - let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); - let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); - let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); - let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); - - Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) - } - - /// Subtracts another element from this element. - #[inline] - pub const fn sub(&self, rhs: &Self) -> Self { - let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); - let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); - let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); - let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); - - // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise - // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. - let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); - let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); - let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); - let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); - - Fr([d0, d1, d2, d3]) - } - - /// Adds this element to another element. - #[inline] - pub const fn add(&self, rhs: &Self) -> Self { - 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, _) = adc(self.0[3], rhs.0[3], carry); - - // Attempt to subtract the modulus, to ensure the value - // is smaller than the modulus. - (&Fr([d0, d1, d2, d3])).sub(&MODULUS) - } - - /// Negates this element. - #[inline] - pub const fn neg(&self) -> Self { - // Subtract `self` from `MODULUS` to negate. Ignore the final - // borrow because it cannot underflow; self is guaranteed to - // be in the field. - let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); - let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); - let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); - let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); - - // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is - // zero if `self` was zero, and `u64::max_value()` if self was nonzero. - let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); - - Fr([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) - } -} - -impl From for [u8; 32] { - fn from(value: Fr) -> [u8; 32] { - value.to_bytes() - } -} - -impl<'a> From<&'a Fr> for [u8; 32] { - fn from(value: &'a Fr) -> [u8; 32] { - value.to_bytes() - } -} - -impl Field for Fr { - fn random(rng: &mut R) -> Self { - let mut buf = [0; 64]; - rng.fill_bytes(&mut buf); - Self::from_bytes_wide(&buf) - } - - fn zero() -> Self { - Self::zero() - } - - fn one() -> Self { - Self::one() - } - - fn is_zero(&self) -> bool { - self.ct_eq(&Self::zero()).into() - } - - #[must_use] - fn square(&self) -> Self { - self.square() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } - - fn invert(&self) -> CtOption { - self.invert() - } - - fn sqrt(&self) -> CtOption { - self.sqrt() - } -} - -impl PrimeField for Fr { - type Repr = [u8; 32]; - type ReprEndianness = byteorder::LittleEndian; - - fn from_repr(r: Self::Repr) -> Option { - let res = Self::from_bytes(&r); - if res.is_some().into() { - Some(res.unwrap()) - } else { - None - } - } - - fn to_repr(&self) -> Self::Repr { - self.to_bytes() - } - - fn is_odd(&self) -> bool { - self.to_bytes()[0] & 1 == 1 - } - - fn char() -> Self::Repr { - MODULUS_BYTES - } - - const NUM_BITS: u32 = MODULUS_BITS; - const CAPACITY: u32 = Self::NUM_BITS - 1; - - fn multiplicative_generator() -> Self { - GENERATOR - } - - const S: u32 = S; - - fn root_of_unity() -> Self { - ROOT_OF_UNITY - } -} - -#[test] -fn test_inv() { - // Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating - // by totient(2**64) - 1 - - let mut inv = 1u64; - for _ in 0..63 { - inv = inv.wrapping_mul(inv); - inv = inv.wrapping_mul(MODULUS.0[0]); - } - inv = inv.wrapping_neg(); - - assert_eq!(inv, INV); -} - -#[test] -fn test_debug() { - assert_eq!( - format!("{:?}", Fr::zero()), - "0x0000000000000000000000000000000000000000000000000000000000000000" - ); - assert_eq!( - format!("{:?}", Fr::one()), - "0x0000000000000000000000000000000000000000000000000000000000000001" - ); - assert_eq!( - format!("{:?}", R2), - "0x09a6fc6f479155c6932514eeeb8814f4f315d62f66b6e75025f80bb3b99607d9" - ); -} - -#[test] -fn test_equality() { - assert_eq!(Fr::zero(), Fr::zero()); - assert_eq!(Fr::one(), Fr::one()); - assert_eq!(R2, R2); - - assert!(Fr::zero() != Fr::one()); - assert!(Fr::one() != R2); -} - -#[test] -fn test_to_bytes() { - assert_eq!( - Fr::zero().to_bytes(), - [ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - Fr::one().to_bytes(), - [ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ] - ); - - assert_eq!( - R2.to_bytes(), - [ - 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, - 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 - ] - ); - - assert_eq!( - (-&Fr::one()).to_bytes(), - [ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ] - ); -} - -#[test] -fn test_from_bytes() { - assert_eq!( - Fr::from_bytes(&[ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Fr::zero() - ); - - assert_eq!( - Fr::from_bytes(&[ - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 - ]) - .unwrap(), - Fr::one() - ); - - assert_eq!( - Fr::from_bytes(&[ - 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, - 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 - ]) - .unwrap(), - R2 - ); - - // -1 should work - assert!(bool::from( - Fr::from_bytes(&[ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_some() - )); - - // modulus is invalid - assert!(bool::from( - Fr::from_bytes(&[ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_none() - )); - - // Anything larger than the modulus is invalid - assert!(bool::from( - Fr::from_bytes(&[ - 184, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_none() - )); - - assert!(bool::from( - Fr::from_bytes(&[ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 104, 6, 169, 175, 51, 101, 234, 180, 125, 14 - ]) - .is_none() - )); - - assert!(bool::from( - Fr::from_bytes(&[ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 15 - ]) - .is_none() - )); -} - -#[test] -fn test_from_u512_zero() { - assert_eq!( - Fr::zero(), - Fr::from_u512([ - MODULUS.0[0], - MODULUS.0[1], - MODULUS.0[2], - MODULUS.0[3], - 0, - 0, - 0, - 0 - ]) - ); -} - -#[test] -fn test_from_u512_r() { - assert_eq!(R, Fr::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); -} - -#[test] -fn test_from_u512_r2() { - assert_eq!(R2, Fr::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); -} - -#[test] -fn test_from_u512_max() { - let max_u64 = 0xffff_ffff_ffff_ffff; - assert_eq!( - R3 - R, - Fr::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) - ); -} - -#[test] -fn test_from_bytes_wide_r2() { - assert_eq!( - R2, - Fr::from_bytes_wide(&[ - 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, - 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_negative_one() { - assert_eq!( - -&Fr::one(), - Fr::from_bytes_wide(&[ - 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, - 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - ]) - ); -} - -#[test] -fn test_from_bytes_wide_maximum() { - assert_eq!( - Fr([ - 0x8b75_c901_5ae4_2a22, - 0xe590_82e7_bf9e_38b8, - 0x6440_c912_61da_51b3, - 0x0a5e_07ff_b209_91cf, - ]), - Fr::from_bytes_wide(&[0xff; 64]) - ); -} - -#[test] -fn test_zero() { - assert_eq!(Fr::zero(), -&Fr::zero()); - assert_eq!(Fr::zero(), Fr::zero() + Fr::zero()); - assert_eq!(Fr::zero(), Fr::zero() - Fr::zero()); - assert_eq!(Fr::zero(), Fr::zero() * Fr::zero()); -} - -#[cfg(test)] -const LARGEST: Fr = Fr([ - 0xd097_0e5e_d6f7_2cb6, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, -]); - -#[test] -fn test_addition() { - let mut tmp = LARGEST; - tmp += &LARGEST; - - assert_eq!( - tmp, - Fr([ - 0xd097_0e5e_d6f7_2cb5, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9 - ]) - ); - - let mut tmp = LARGEST; - tmp += &Fr([1, 0, 0, 0]); - - assert_eq!(tmp, Fr::zero()); -} - -#[test] -fn test_negation() { - let tmp = -&LARGEST; - - assert_eq!(tmp, Fr([1, 0, 0, 0])); - - let tmp = -&Fr::zero(); - assert_eq!(tmp, Fr::zero()); - let tmp = -&Fr([1, 0, 0, 0]); - assert_eq!(tmp, LARGEST); -} - -#[test] -fn test_subtraction() { - let mut tmp = LARGEST; - tmp -= &LARGEST; - - assert_eq!(tmp, Fr::zero()); - - let mut tmp = Fr::zero(); - tmp -= &LARGEST; - - let mut tmp2 = MODULUS; - tmp2 -= &LARGEST; - - assert_eq!(tmp, tmp2); -} - -#[test] -fn test_multiplication() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp *= &cur; - - let mut tmp2 = Fr::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_squaring() { - let mut cur = LARGEST; - - for _ in 0..100 { - let mut tmp = cur; - tmp = tmp.square(); - - let mut tmp2 = Fr::zero(); - for b in cur - .to_bytes() - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) - { - let tmp3 = tmp2; - tmp2.add_assign(&tmp3); - - if b { - tmp2.add_assign(&cur); - } - } - - assert_eq!(tmp, tmp2); - - cur.add_assign(&LARGEST); - } -} - -#[test] -fn test_inversion() { - assert!(bool::from(Fr::zero().invert().is_none())); - assert_eq!(Fr::one().invert().unwrap(), Fr::one()); - assert_eq!((-&Fr::one()).invert().unwrap(), -&Fr::one()); - - let mut tmp = R2; - - for _ in 0..100 { - let mut tmp2 = tmp.invert().unwrap(); - tmp2.mul_assign(&tmp); - - assert_eq!(tmp2, Fr::one()); - - tmp.add_assign(&R2); - } -} - -#[test] -fn test_invert_is_pow() { - let r_minus_2 = [ - 0xd097_0e5e_d6f7_2cb5, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, - ]; - - let mut r1 = R; - let mut r2 = R; - let mut r3 = R; - - for _ in 0..100 { - r1 = r1.invert().unwrap(); - r2 = r2.pow_vartime(&r_minus_2); - r3 = r3.pow(&r_minus_2); - - assert_eq!(r1, r2); - assert_eq!(r2, r3); - // Add R so we check something different next time around - r1.add_assign(&R); - r2 = r1; - r3 = r1; - } -} - -#[test] -fn test_sqrt() { - let mut square = Fr([ - // r - 2 - 0xd097_0e5e_d6f7_2cb5, - 0xa668_2093_ccc8_1082, - 0x0667_3b01_0134_3b00, - 0x0e7d_b4ea_6533_afa9, - ]); - - let mut none_count = 0; - - for _ in 0..100 { - let square_root = square.sqrt(); - if bool::from(square_root.is_none()) { - none_count += 1; - } else { - assert_eq!(square_root.unwrap() * square_root.unwrap(), square); - } - square -= Fr::one(); - } - - assert_eq!(47, none_count); -} - -#[test] -fn test_from_raw() { - assert_eq!( - Fr::from_raw([ - 0x25f8_0bb3_b996_07d8, - 0xf315_d62f_66b6_e750, - 0x9325_14ee_eb88_14f4, - 0x09a6_fc6f_4791_55c6, - ]), - Fr::from_raw([0xffff_ffff_ffff_ffff; 4]) - ); - - assert_eq!(Fr::from_raw(MODULUS.0), Fr::zero()); - - assert_eq!(Fr::from_raw([1, 0, 0, 0]), R); -} diff --git a/jubjub/src/lib.rs b/jubjub/src/lib.rs deleted file mode 100644 index d55dd6c9b..000000000 --- a/jubjub/src/lib.rs +++ /dev/null @@ -1,1756 +0,0 @@ -//! This crate provides an implementation of the **Jubjub** elliptic curve and its associated -//! field arithmetic. See [`README.md`](https://github.com/zkcrypto/jubjub/blob/master/README.md) for more details about Jubjub. -//! -//! # API -//! -//! * `AffinePoint` / `ExtendedPoint` which are implementations of Jubjub group arithmetic -//! * `AffineNielsPoint` / `ExtendedNielsPoint` which are pre-processed Jubjub points -//! * `Fq`, which is the base field of Jubjub -//! * `Fr`, which is the scalar field of Jubjub -//! * `batch_normalize` for converting many `ExtendedPoint`s into `AffinePoint`s efficiently. -//! -//! # Constant Time -//! -//! All operations are constant time unless explicitly noted; these functions will contain -//! "vartime" in their name and they will be documented as variable time. -//! -//! This crate uses the `subtle` crate to perform constant-time operations. - -#![no_std] -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] -#![deny(missing_debug_implementations)] -#![deny(missing_docs)] -#![deny(unsafe_code)] -// 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 will necessarily involve various binary -// operators, and so this lint is triggered unnecessarily. -#![allow(clippy::suspicious_arithmetic_impl)] - -#[cfg(test)] -#[macro_use] -extern crate std; - -use core::borrow::Borrow; -use core::fmt; -use core::iter::Sum; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; -use ff::Field; -use group::{ - cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, - prime::PrimeGroup, - Curve, Group, GroupEncoding, WnafGroup, -}; -use rand_core::RngCore; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; - -#[macro_use] -mod util; - -mod fr; -pub use bls12_381::Scalar as Fq; -pub use fr::Fr; - -/// A better name than Fr. -pub type Scalar = Fr; - -const FR_MODULUS_BYTES: [u8; 32] = [ - 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, - 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, -]; - -/// This represents a Jubjub point in the affine `(u, v)` -/// coordinates. -#[derive(Clone, Copy, Debug, Eq)] -pub struct AffinePoint { - u: Fq, - v: Fq, -} - -impl fmt::Display for AffinePoint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl Neg for AffinePoint { - type Output = AffinePoint; - - /// This computes the negation of a point `P = (u, v)` - /// as `-P = (-u, v)`. - #[inline] - fn neg(self) -> AffinePoint { - AffinePoint { - u: -self.u, - v: self.v, - } - } -} - -impl ConstantTimeEq for AffinePoint { - fn ct_eq(&self, other: &Self) -> Choice { - self.u.ct_eq(&other.u) & self.v.ct_eq(&other.v) - } -} - -impl PartialEq for AffinePoint { - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl ConditionallySelectable for AffinePoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - AffinePoint { - u: Fq::conditional_select(&a.u, &b.u, choice), - v: Fq::conditional_select(&a.v, &b.v, choice), - } - } -} - -/// This represents an extended point `(U, V, Z, T1, T2)` -/// with `Z` nonzero, corresponding to the affine point -/// `(U/Z, V/Z)`. We always have `T1 * T2 = UV/Z`. -/// -/// You can do the following things with a point in this -/// form: -/// -/// * Convert it into a point in the affine form. -/// * Add it to an `ExtendedPoint`, `AffineNielsPoint` or `ExtendedNielsPoint`. -/// * Double it using `double()`. -/// * Compare it with another extended point using `PartialEq` or `ct_eq()`. -#[derive(Clone, Copy, Debug, Eq)] -pub struct ExtendedPoint { - u: Fq, - v: Fq, - z: Fq, - t1: Fq, - t2: Fq, -} - -impl fmt::Display for ExtendedPoint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - } -} - -impl ConstantTimeEq for ExtendedPoint { - fn ct_eq(&self, other: &Self) -> Choice { - // (u/z, v/z) = (u'/z', v'/z') is implied by - // (uz'z = u'z'z) and - // (vz'z = v'z'z) - // as z and z' are always nonzero. - - (self.u * other.z).ct_eq(&(other.u * self.z)) - & (self.v * other.z).ct_eq(&(other.v * self.z)) - } -} - -impl ConditionallySelectable for ExtendedPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ExtendedPoint { - u: Fq::conditional_select(&a.u, &b.u, choice), - v: Fq::conditional_select(&a.v, &b.v, choice), - z: Fq::conditional_select(&a.z, &b.z, choice), - t1: Fq::conditional_select(&a.t1, &b.t1, choice), - t2: Fq::conditional_select(&a.t2, &b.t2, choice), - } - } -} - -impl PartialEq for ExtendedPoint { - fn eq(&self, other: &Self) -> bool { - bool::from(self.ct_eq(other)) - } -} - -impl Sum for ExtendedPoint -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl Neg for ExtendedPoint { - type Output = ExtendedPoint; - - /// Computes the negation of a point `P = (U, V, Z, T)` - /// as `-P = (-U, V, Z, -T1, T2)`. The choice of `T1` - /// is made without loss of generality. - #[inline] - fn neg(self) -> ExtendedPoint { - ExtendedPoint { - u: -self.u, - v: self.v, - z: self.z, - t1: -self.t1, - t2: self.t2, - } - } -} - -impl From for ExtendedPoint { - /// Constructs an extended point (with `Z = 1`) from - /// an affine point using the map `(u, v) => (u, v, 1, u, v)`. - fn from(affine: AffinePoint) -> ExtendedPoint { - ExtendedPoint { - u: affine.u, - v: affine.v, - z: Fq::one(), - t1: affine.u, - t2: affine.v, - } - } -} - -impl<'a> From<&'a ExtendedPoint> for AffinePoint { - /// Constructs an affine point from an extended point - /// using the map `(U, V, Z, T1, T2) => (U/Z, V/Z)` - /// as Z is always nonzero. **This requires a field inversion - /// and so it is recommended to perform these in a batch - /// using [`batch_normalize`](crate::batch_normalize) instead.** - fn from(extended: &'a ExtendedPoint) -> AffinePoint { - // Z coordinate is always nonzero, so this is - // its inverse. - let zinv = extended.z.invert().unwrap(); - - AffinePoint { - u: extended.u * zinv, - v: extended.v * zinv, - } - } -} - -impl From for AffinePoint { - fn from(extended: ExtendedPoint) -> AffinePoint { - AffinePoint::from(&extended) - } -} - -/// This is a pre-processed version of an affine point `(u, v)` -/// in the form `(v + u, v - u, u * v * 2d)`. This can be added to an -/// [`ExtendedPoint`](crate::ExtendedPoint). -#[derive(Clone, Copy, Debug)] -pub struct AffineNielsPoint { - v_plus_u: Fq, - v_minus_u: Fq, - t2d: Fq, -} - -impl AffineNielsPoint { - /// Constructs this point from the neutral element `(0, 1)`. - pub const fn identity() -> Self { - AffineNielsPoint { - v_plus_u: Fq::one(), - v_minus_u: Fq::one(), - t2d: Fq::zero(), - } - } - - #[inline] - fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { - let zero = AffineNielsPoint::identity(); - - let mut acc = ExtendedPoint::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading four bits because they're always - // unset for Fr. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(4) - { - acc = acc.double(); - acc += AffineNielsPoint::conditional_select(&zero, &self, bit); - } - - acc - } - - /// Multiplies this point by the specific little-endian bit pattern in the - /// given byte array, ignoring the highest four bits. - pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { - self.multiply(by) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a AffineNielsPoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative_mixed!(AffineNielsPoint, Fr, ExtendedPoint); - -impl ConditionallySelectable for AffineNielsPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - AffineNielsPoint { - v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), - v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), - t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), - } - } -} - -/// This is a pre-processed version of an extended point `(U, V, Z, T1, T2)` -/// in the form `(V + U, V - U, Z, T1 * T2 * 2d)`. -#[derive(Clone, Copy, Debug)] -pub struct ExtendedNielsPoint { - v_plus_u: Fq, - v_minus_u: Fq, - z: Fq, - t2d: Fq, -} - -impl ConditionallySelectable for ExtendedNielsPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - ExtendedNielsPoint { - v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), - v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), - z: Fq::conditional_select(&a.z, &b.z, choice), - t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), - } - } -} - -impl ExtendedNielsPoint { - /// Constructs this point from the neutral element `(0, 1)`. - pub const fn identity() -> Self { - ExtendedNielsPoint { - v_plus_u: Fq::one(), - v_minus_u: Fq::one(), - z: Fq::one(), - t2d: Fq::zero(), - } - } - - #[inline] - fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { - let zero = ExtendedNielsPoint::identity(); - - let mut acc = ExtendedPoint::identity(); - - // This is a simple double-and-add implementation of point - // multiplication, moving from most significant to least - // significant bit of the scalar. - // - // We skip the leading four bits because they're always - // unset for Fr. - for bit in by - .iter() - .rev() - .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) - .skip(4) - { - acc = acc.double(); - acc += ExtendedNielsPoint::conditional_select(&zero, &self, bit); - } - - acc - } - - /// Multiplies this point by the specific little-endian bit pattern in the - /// given byte array, ignoring the highest four bits. - pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { - self.multiply(by) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, ExtendedPoint); - -// `d = -(10240/10241)` -const EDWARDS_D: Fq = Fq::from_raw([ - 0x0106_5fd6_d634_3eb1, - 0x292d_7f6d_3757_9d26, - 0xf5fd_9207_e6bd_7fd4, - 0x2a93_18e7_4bfa_2b48, -]); - -// `2*d` -const EDWARDS_D2: Fq = Fq::from_raw([ - 0x020c_bfad_ac68_7d62, - 0x525a_feda_6eaf_3a4c, - 0xebfb_240f_cd7a_ffa8, - 0x5526_31ce_97f4_5691, -]); - -impl AffinePoint { - /// Constructs the neutral element `(0, 1)`. - pub const fn identity() -> Self { - AffinePoint { - u: Fq::zero(), - v: Fq::one(), - } - } - - /// Determines if this point is the identity. - pub fn is_identity(&self) -> Choice { - ExtendedPoint::from(*self).is_identity() - } - - /// Multiplies this point by the cofactor, producing an - /// `ExtendedPoint` - pub fn mul_by_cofactor(&self) -> ExtendedPoint { - ExtendedPoint::from(*self).mul_by_cofactor() - } - - /// Determines if this point is of small order. - pub fn is_small_order(&self) -> Choice { - ExtendedPoint::from(*self).is_small_order() - } - - /// Determines if this point is torsion free and so is - /// in the prime order subgroup. - pub fn is_torsion_free(&self) -> Choice { - ExtendedPoint::from(*self).is_torsion_free() - } - - /// Determines if this point is prime order, or in other words that - /// the smallest scalar multiplied by this point that produces the - /// identity is `r`. This is equivalent to checking that the point - /// is both torsion free and not the identity. - pub fn is_prime_order(&self) -> Choice { - let extended = ExtendedPoint::from(*self); - extended.is_torsion_free() & (!extended.is_identity()) - } - - /// Converts this element into its byte representation. - pub fn to_bytes(&self) -> [u8; 32] { - let mut tmp = self.v.to_bytes(); - let u = self.u.to_bytes(); - - // Encode the sign of the u-coordinate in the most - // significant bit. - tmp[31] |= u[0] << 7; - - tmp - } - - /// Attempts to interpret a byte representation of an - /// affine point, failing if the element is not on - /// the curve or non-canonical. - pub fn from_bytes(mut b: [u8; 32]) -> CtOption { - // Grab the sign bit from the representation - let sign = b[31] >> 7; - - // Mask away the sign bit - b[31] &= 0b0111_1111; - - // Interpret what remains as the v-coordinate - Fq::from_bytes(&b).and_then(|v| { - // -u^2 + v^2 = 1 + d.u^2.v^2 - // -u^2 = 1 + d.u^2.v^2 - v^2 (rearrange) - // -u^2 - d.u^2.v^2 = 1 - v^2 (rearrange) - // u^2 + d.u^2.v^2 = v^2 - 1 (flip signs) - // u^2 (1 + d.v^2) = v^2 - 1 (factor) - // u^2 = (v^2 - 1) / (1 + d.v^2) (isolate u^2) - // We know that (1 + d.v^2) is nonzero for all v: - // (1 + d.v^2) = 0 - // d.v^2 = -1 - // v^2 = -(1 / d) No solutions, as -(1 / d) is not a square - - let v2 = v.square(); - - ((v2 - Fq::one()) * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) - .sqrt() - .and_then(|u| { - // Fix the sign of `u` if necessary - let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); - let u_negated = -u; - let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); - - CtOption::new(AffinePoint { u: final_u, v }, Choice::from(1u8)) - }) - }) - } - - /// Returns the `u`-coordinate of this point. - pub fn get_u(&self) -> Fq { - self.u - } - - /// Returns the `v`-coordinate of this point. - pub fn get_v(&self) -> Fq { - self.v - } - - /// Returns an `ExtendedPoint` for use in arithmetic operations. - pub const fn to_extended(&self) -> ExtendedPoint { - ExtendedPoint { - u: self.u, - v: self.v, - z: Fq::one(), - t1: self.u, - t2: self.v, - } - } - - /// Performs a pre-processing step that produces an `AffineNielsPoint` - /// for use in multiple additions. - pub const fn to_niels(&self) -> AffineNielsPoint { - AffineNielsPoint { - v_plus_u: Fq::add(&self.v, &self.u), - v_minus_u: Fq::sub(&self.v, &self.u), - t2d: Fq::mul(&Fq::mul(&self.u, &self.v), &EDWARDS_D2), - } - } - - /// Constructs an AffinePoint given `u` and `v` without checking - /// that the point is on the curve. - pub const fn from_raw_unchecked(u: Fq, v: Fq) -> AffinePoint { - AffinePoint { u, v } - } - - /// This is only for debugging purposes and not - /// exposed in the public API. Checks that this - /// point is on the curve. - #[cfg(test)] - fn is_on_curve_vartime(&self) -> bool { - let u2 = self.u.square(); - let v2 = self.v.square(); - - v2 - u2 == Fq::one() + EDWARDS_D * u2 * v2 - } -} - -impl ExtendedPoint { - /// Constructs an extended point from the neutral element `(0, 1)`. - pub const fn identity() -> Self { - ExtendedPoint { - u: Fq::zero(), - v: Fq::one(), - z: Fq::one(), - t1: Fq::zero(), - t2: Fq::zero(), - } - } - - /// Determines if this point is the identity. - pub fn is_identity(&self) -> Choice { - // If this point is the identity, then - // u = 0 * z = 0 - // and v = 1 * z = z - self.u.ct_eq(&Fq::zero()) & self.v.ct_eq(&self.z) - } - - /// Determines if this point is of small order. - pub fn is_small_order(&self) -> Choice { - // We only need to perform two doublings, since the 2-torsion - // points are (0, 1) and (0, -1), and so we only need to check - // that the u-coordinate of the result is zero to see if the - // point is small order. - self.double().double().u.ct_eq(&Fq::zero()) - } - - /// Determines if this point is torsion free and so is contained - /// in the prime order subgroup. - pub fn is_torsion_free(&self) -> Choice { - self.multiply(&FR_MODULUS_BYTES).is_identity() - } - - /// Determines if this point is prime order, or in other words that - /// the smallest scalar multiplied by this point that produces the - /// identity is `r`. This is equivalent to checking that the point - /// is both torsion free and not the identity. - pub fn is_prime_order(&self) -> Choice { - self.is_torsion_free() & (!self.is_identity()) - } - - /// Multiplies this element by the cofactor `8`. - pub fn mul_by_cofactor(&self) -> ExtendedPoint { - self.double().double().double() - } - - /// Performs a pre-processing step that produces an `ExtendedNielsPoint` - /// for use in multiple additions. - pub fn to_niels(&self) -> ExtendedNielsPoint { - ExtendedNielsPoint { - v_plus_u: self.v + self.u, - v_minus_u: self.v - self.u, - z: self.z, - t2d: self.t1 * self.t2 * EDWARDS_D2, - } - } - - /// Computes the doubling of a point more efficiently than a point can - /// be added to itself. - pub fn double(&self) -> ExtendedPoint { - // Doubling is more efficient (three multiplications, four squarings) - // when we work within the projective coordinate space (U:Z, V:Z). We - // rely on the most efficient formula, "dbl-2008-bbjlp", as described - // in Section 6 of "Twisted Edwards Curves" by Bernstein et al. - // - // See - // for more information. - // - // We differ from the literature in that we use (u, v) rather than - // (x, y) coordinates. We also have the constant `a = -1` implied. Let - // us rewrite the procedure of doubling (u, v, z) to produce (U, V, Z) - // as follows: - // - // B = (u + v)^2 - // C = u^2 - // D = v^2 - // F = D - C - // H = 2 * z^2 - // J = F - H - // U = (B - C - D) * J - // V = F * (- C - D) - // Z = F * J - // - // If we compute K = D + C, we can rewrite this: - // - // B = (u + v)^2 - // C = u^2 - // D = v^2 - // F = D - C - // K = D + C - // H = 2 * z^2 - // J = F - H - // U = (B - K) * J - // V = F * (-K) - // Z = F * J - // - // In order to avoid the unnecessary negation of K, - // we will negate J, transforming the result into - // an equivalent point with a negated z-coordinate. - // - // B = (u + v)^2 - // C = u^2 - // D = v^2 - // F = D - C - // K = D + C - // H = 2 * z^2 - // J = H - F - // U = (B - K) * J - // V = F * K - // Z = F * J - // - // Let us rename some variables to simplify: - // - // UV2 = (u + v)^2 - // UU = u^2 - // VV = v^2 - // VVmUU = VV - UU - // VVpUU = VV + UU - // ZZ2 = 2 * z^2 - // J = ZZ2 - VVmUU - // U = (UV2 - VVpUU) * J - // V = VVmUU * VVpUU - // Z = VVmUU * J - // - // We wish to obtain two factors of T = UV/Z. - // - // UV/Z = (UV2 - VVpUU) * (ZZ2 - VVmUU) * VVmUU * VVpUU / VVmUU / (ZZ2 - VVmUU) - // = (UV2 - VVpUU) * VVmUU * VVpUU / VVmUU - // = (UV2 - VVpUU) * VVpUU - // - // and so we have that T1 = (UV2 - VVpUU) and T2 = VVpUU. - - let uu = self.u.square(); - let vv = self.v.square(); - let zz2 = self.z.square().double(); - let uv2 = (self.u + self.v).square(); - let vv_plus_uu = vv + uu; - let vv_minus_uu = vv - uu; - - // The remaining arithmetic is exactly the process of converting - // from a completed point to an extended point. - CompletedPoint { - u: uv2 - vv_plus_uu, - v: vv_plus_uu, - z: vv_minus_uu, - t: zz2 - vv_minus_uu, - } - .into_extended() - } - - #[inline] - fn multiply(self, by: &[u8; 32]) -> Self { - self.to_niels().multiply(by) - } - - /// Converts a batch of projective elements into affine elements. - /// - /// This function will panic if `p.len() != q.len()`. - /// - /// This costs 5 multiplications per element, and a field inversion. - fn batch_normalize(p: &[Self], q: &mut [AffinePoint]) { - assert_eq!(p.len(), q.len()); - - let mut acc = Fq::one(); - for (p, q) in p.iter().zip(q.iter_mut()) { - // We use the `u` field of `AffinePoint` to store the product - // of previous z-coordinates seen. - q.u = acc; - acc *= &p.z; - } - - // This is the inverse, as all z-coordinates are nonzero. - acc = acc.invert().unwrap(); - - for (p, q) in p.iter().zip(q.iter_mut()).rev() { - // Compute tmp = 1/z - let tmp = q.u * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc *= &p.z; - - // Set the coordinates to the correct value - q.u = p.u * &tmp; // Multiply by 1/z - q.v = p.v * &tmp; // Multiply by 1/z - } - } - - /// This is only for debugging purposes and not - /// exposed in the public API. Checks that this - /// point is on the curve. - #[cfg(test)] - fn is_on_curve_vartime(&self) -> bool { - let affine = AffinePoint::from(*self); - - self.z != Fq::zero() - && affine.is_on_curve_vartime() - && (affine.u * affine.v * self.z == self.t1 * self.t2) - } -} - -impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative!(ExtendedPoint, Fr); - -impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn add(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { - // We perform addition in the extended coordinates. Here we use - // a formula presented by Hisil, Wong, Carter and Dawson in - // "Twisted Edward Curves Revisited" which only requires 8M. - // - // A = (V1 - U1) * (V2 - U2) - // B = (V1 + U1) * (V2 + U2) - // C = 2d * T1 * T2 - // D = 2 * Z1 * Z2 - // E = B - A - // F = D - C - // G = D + C - // H = B + A - // U3 = E * F - // Y3 = G * H - // Z3 = F * G - // T3 = E * H - - let a = (self.v - self.u) * other.v_minus_u; - let b = (self.v + self.u) * other.v_plus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = (self.z * other.z).double(); - - // The remaining arithmetic is exactly the process of converting - // from a completed point to an extended point. - CompletedPoint { - u: b - a, - v: b + a, - z: d + c, - t: d - c, - } - .into_extended() - } -} - -impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { - let a = (self.v - self.u) * other.v_plus_u; - let b = (self.v + self.u) * other.v_minus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = (self.z * other.z).double(); - - CompletedPoint { - u: b - a, - v: b + a, - z: d - c, - t: d + c, - } - .into_extended() - } -} - -impl_binops_additive!(ExtendedPoint, ExtendedNielsPoint); - -impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn add(self, other: &'b AffineNielsPoint) -> ExtendedPoint { - // This is identical to the addition formula for `ExtendedNielsPoint`, - // except we can assume that `other.z` is one, so that we perform - // 7 multiplications. - - let a = (self.v - self.u) * other.v_minus_u; - let b = (self.v + self.u) * other.v_plus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = self.z.double(); - - // The remaining arithmetic is exactly the process of converting - // from a completed point to an extended point. - CompletedPoint { - u: b - a, - v: b + a, - z: d + c, - t: d - c, - } - .into_extended() - } -} - -impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[allow(clippy::suspicious_arithmetic_impl)] - fn sub(self, other: &'b AffineNielsPoint) -> ExtendedPoint { - let a = (self.v - self.u) * other.v_plus_u; - let b = (self.v + self.u) * other.v_minus_u; - let c = self.t1 * self.t2 * other.t2d; - let d = self.z.double(); - - CompletedPoint { - u: b - a, - v: b + a, - z: d - c, - t: d + c, - } - .into_extended() - } -} - -impl_binops_additive!(ExtendedPoint, AffineNielsPoint); - -impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint { - self + other.to_niels() - } -} - -impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint { - self - other.to_niels() - } -} - -impl_binops_additive!(ExtendedPoint, ExtendedPoint); - -impl<'a, 'b> Add<&'b AffinePoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn add(self, other: &'b AffinePoint) -> ExtendedPoint { - self + other.to_niels() - } -} - -impl<'a, 'b> Sub<&'b AffinePoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn sub(self, other: &'b AffinePoint) -> ExtendedPoint { - self - other.to_niels() - } -} - -impl_binops_additive!(ExtendedPoint, AffinePoint); - -/// This is a "completed" point produced during a point doubling or -/// addition routine. These points exist in the `(U:Z, V:T)` model -/// of the curve. This is not exposed in the API because it is -/// an implementation detail. -struct CompletedPoint { - u: Fq, - v: Fq, - z: Fq, - t: Fq, -} - -impl CompletedPoint { - /// This converts a completed point into an extended point by - /// homogenizing: - /// - /// (u/z, v/t) = (u/z * t/t, v/t * z/z) = (ut/zt, vz/zt) - /// - /// The resulting T coordinate is utvz/zt = uv, and so - /// T1 = u, T2 = v, without loss of generality. - #[inline] - fn into_extended(self) -> ExtendedPoint { - ExtendedPoint { - u: self.u * self.t, - v: self.v * self.z, - z: self.z * self.t, - t1: self.u, - t2: self.v, - } - } -} - -impl Default for AffinePoint { - /// Returns the identity. - fn default() -> AffinePoint { - AffinePoint::identity() - } -} - -impl Default for ExtendedPoint { - /// Returns the identity. - fn default() -> ExtendedPoint { - ExtendedPoint::identity() - } -} - -/// This takes a mutable slice of `ExtendedPoint`s and "normalizes" them using -/// only a single inversion for the entire batch. This normalization results in -/// all of the points having a Z-coordinate of one. Further, an iterator is -/// returned which can be used to obtain `AffinePoint`s for each element in the -/// slice. -/// -/// This costs 5 multiplications per element, and a field inversion. -pub fn batch_normalize<'a>(v: &'a mut [ExtendedPoint]) -> impl Iterator + 'a { - let mut acc = Fq::one(); - for p in v.iter_mut() { - // We use the `t1` field of `ExtendedPoint` to store the product - // of previous z-coordinates seen. - p.t1 = acc; - acc *= &p.z; - } - - // This is the inverse, as all z-coordinates are nonzero. - acc = acc.invert().unwrap(); - - for p in v.iter_mut().rev() { - let mut q = *p; - - // Compute tmp = 1/z - let tmp = q.t1 * acc; - - // Cancel out z-coordinate in denominator of `acc` - acc *= &q.z; - - // Set the coordinates to the correct value - q.u *= &tmp; // Multiply by 1/z - q.v *= &tmp; // Multiply by 1/z - q.z = Fq::one(); // z-coordinate is now one - q.t1 = q.u; - q.t2 = q.v; - - *p = q; - } - - // All extended points are now normalized, but the type - // doesn't encode this fact. Let us offer affine points - // to the caller. - - v.iter().map(|p| AffinePoint { u: p.u, v: p.v }) -} - -impl<'a, 'b> Mul<&'b Fr> for &'a AffinePoint { - type Output = ExtendedPoint; - - fn mul(self, other: &'b Fr) -> ExtendedPoint { - self.to_niels().multiply(&other.to_bytes()) - } -} - -impl_binops_multiplicative_mixed!(AffinePoint, Fr, ExtendedPoint); - -/// This represents a point in the prime-order subgroup of Jubjub, in extended -/// coordinates. -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub struct SubgroupPoint(ExtendedPoint); - -impl From for ExtendedPoint { - fn from(val: SubgroupPoint) -> ExtendedPoint { - val.0 - } -} - -impl<'a> From<&'a SubgroupPoint> for &'a ExtendedPoint { - fn from(val: &'a SubgroupPoint) -> &'a ExtendedPoint { - &val.0 - } -} - -impl fmt::Display for SubgroupPoint { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.0) - } -} - -impl ConditionallySelectable for SubgroupPoint { - fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - SubgroupPoint(ExtendedPoint::conditional_select(&a.0, &b.0, choice)) - } -} - -impl SubgroupPoint { - /// Constructs an AffinePoint given `u` and `v` without checking that the point is on - /// the curve or in the prime-order subgroup. - /// - /// This should only be used for hard-coding constants (e.g. fixed generators); in all - /// other cases, use [`SubgroupPoint::from_bytes`] instead. - /// - /// [`SubgroupPoint::from_bytes`]: SubgroupPoint#impl-GroupEncoding - pub const fn from_raw_unchecked(u: Fq, v: Fq) -> Self { - SubgroupPoint(AffinePoint::from_raw_unchecked(u, v).to_extended()) - } -} - -impl Sum for SubgroupPoint -where - T: Borrow, -{ - fn sum(iter: I) -> Self - where - I: Iterator, - { - iter.fold(Self::identity(), |acc, item| acc + item.borrow()) - } -} - -impl Neg for SubgroupPoint { - type Output = SubgroupPoint; - - #[inline] - fn neg(self) -> SubgroupPoint { - SubgroupPoint(-self.0) - } -} - -impl Neg for &SubgroupPoint { - type Output = SubgroupPoint; - - #[inline] - fn neg(self) -> SubgroupPoint { - SubgroupPoint(-self.0) - } -} - -impl<'a, 'b> Add<&'b SubgroupPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn add(self, other: &'b SubgroupPoint) -> ExtendedPoint { - self + &other.0 - } -} - -impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a ExtendedPoint { - type Output = ExtendedPoint; - - #[inline] - fn sub(self, other: &'b SubgroupPoint) -> ExtendedPoint { - self - &other.0 - } -} - -impl_binops_additive!(ExtendedPoint, SubgroupPoint); - -impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint { - type Output = SubgroupPoint; - - #[inline] - fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint { - SubgroupPoint(self.0 + &other.0) - } -} - -impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint { - type Output = SubgroupPoint; - - #[inline] - fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint { - SubgroupPoint(self.0 - &other.0) - } -} - -impl_binops_additive!(SubgroupPoint, SubgroupPoint); - -impl<'a, 'b> Mul<&'b Fr> for &'a SubgroupPoint { - type Output = SubgroupPoint; - - fn mul(self, other: &'b Fr) -> SubgroupPoint { - SubgroupPoint(self.0.multiply(&other.to_bytes())) - } -} - -impl_binops_multiplicative!(SubgroupPoint, Fr); - -impl Group for ExtendedPoint { - type Scalar = Fr; - - fn random(rng: &mut R) -> Self { - loop { - let v = Fq::random(rng); - let flip_sign = rng.next_u32() % 2 != 0; - - // See AffinePoint::from_bytes for details. - let v2 = v.square(); - let p = ((v2 - Fq::one()) - * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) - .sqrt() - .map(|u| AffinePoint { - u: if flip_sign { -u } else { u }, - v, - }); - - if p.is_some().into() { - let p = p.unwrap().to_curve(); - - if bool::from(!p.is_identity()) { - return p; - } - } - } - } - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - AffinePoint::generator().into() - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - #[must_use] - fn double(&self) -> Self { - self.double() - } -} - -impl Group for SubgroupPoint { - type Scalar = Fr; - - fn random(rng: &mut R) -> Self { - loop { - let p = ExtendedPoint::random(rng).clear_cofactor(); - - if bool::from(!p.is_identity()) { - return p; - } - } - } - - fn identity() -> Self { - SubgroupPoint(ExtendedPoint::identity()) - } - - fn generator() -> Self { - ExtendedPoint::generator().clear_cofactor() - } - - fn is_identity(&self) -> Choice { - self.0.is_identity() - } - - #[must_use] - fn double(&self) -> Self { - SubgroupPoint(self.0.double()) - } -} - -impl WnafGroup for ExtendedPoint { - fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { - // Copied from bls12_381::g1, should be updated. - const RECOMMENDATIONS: [usize; 12] = - [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; - - let mut ret = 4; - for r in &RECOMMENDATIONS { - if num_scalars > *r { - ret += 1; - } else { - break; - } - } - - ret - } -} - -impl PrimeGroup for SubgroupPoint {} - -impl CofactorGroup for ExtendedPoint { - type Subgroup = SubgroupPoint; - - fn clear_cofactor(&self) -> Self::Subgroup { - SubgroupPoint(self.mul_by_cofactor()) - } - - fn into_subgroup(self) -> CtOption { - CtOption::new(SubgroupPoint(self), self.is_torsion_free()) - } -} - -impl Curve for ExtendedPoint { - type AffineRepr = AffinePoint; - - fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { - Self::batch_normalize(p, q); - } - - fn to_affine(&self) -> Self::AffineRepr { - self.into() - } -} - -impl CofactorCurve for ExtendedPoint { - type Affine = AffinePoint; -} - -impl CofactorCurveAffine for AffinePoint { - type Scalar = Fr; - type Curve = ExtendedPoint; - - fn identity() -> Self { - Self::identity() - } - - fn generator() -> Self { - // The point with the lowest positive v-coordinate and positive u-coordinate. - AffinePoint { - u: Fq::from_raw([ - 0xe4b3_d35d_f1a7_adfe, - 0xcaf5_5d1b_29bf_81af, - 0x8b0f_03dd_d60a_8187, - 0x62ed_cbb8_bf37_87c8, - ]), - v: Fq::from_raw([ - 0x0000_0000_0000_000b, - 0x0000_0000_0000_0000, - 0x0000_0000_0000_0000, - 0x0000_0000_0000_0000, - ]), - } - } - - fn is_identity(&self) -> Choice { - self.is_identity() - } - - fn to_curve(&self) -> Self::Curve { - (*self).into() - } -} - -impl GroupEncoding for ExtendedPoint { - type Repr = [u8; 32]; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - AffinePoint::from_bytes(*bytes).map(Self::from) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - // We can't avoid curve checks when parsing a compressed encoding. - AffinePoint::from_bytes(*bytes).map(Self::from) - } - - fn to_bytes(&self) -> Self::Repr { - AffinePoint::from(self).to_bytes() - } -} - -impl GroupEncoding for SubgroupPoint { - type Repr = [u8; 32]; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - ExtendedPoint::from_bytes(bytes).and_then(|p| p.into_subgroup()) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - ExtendedPoint::from_bytes_unchecked(bytes).map(SubgroupPoint) - } - - fn to_bytes(&self) -> Self::Repr { - self.0.to_bytes() - } -} - -impl GroupEncoding for AffinePoint { - type Repr = [u8; 32]; - - fn from_bytes(bytes: &Self::Repr) -> CtOption { - Self::from_bytes(*bytes) - } - - fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { - Self::from_bytes(*bytes) - } - - fn to_bytes(&self) -> Self::Repr { - self.to_bytes() - } -} - -#[test] -fn test_is_on_curve_var() { - assert!(AffinePoint::identity().is_on_curve_vartime()); -} - -#[test] -fn test_d_is_non_quadratic_residue() { - assert!(bool::from(EDWARDS_D.sqrt().is_none())); - assert!(bool::from((-EDWARDS_D).sqrt().is_none())); - assert!(bool::from((-EDWARDS_D).invert().unwrap().sqrt().is_none())); -} - -#[test] -fn test_affine_niels_point_identity() { - assert_eq!( - AffineNielsPoint::identity().v_plus_u, - AffinePoint::identity().to_niels().v_plus_u - ); - assert_eq!( - AffineNielsPoint::identity().v_minus_u, - AffinePoint::identity().to_niels().v_minus_u - ); - assert_eq!( - AffineNielsPoint::identity().t2d, - AffinePoint::identity().to_niels().t2d - ); -} - -#[test] -fn test_extended_niels_point_identity() { - assert_eq!( - ExtendedNielsPoint::identity().v_plus_u, - ExtendedPoint::identity().to_niels().v_plus_u - ); - assert_eq!( - ExtendedNielsPoint::identity().v_minus_u, - ExtendedPoint::identity().to_niels().v_minus_u - ); - assert_eq!( - ExtendedNielsPoint::identity().z, - ExtendedPoint::identity().to_niels().z - ); - assert_eq!( - ExtendedNielsPoint::identity().t2d, - ExtendedPoint::identity().to_niels().t2d - ); -} - -#[test] -fn test_assoc() { - let p = ExtendedPoint::from(AffinePoint { - u: Fq::from_raw([ - 0x81c5_71e5_d883_cfb0, - 0x049f_7a68_6f14_7029, - 0xf539_c860_bc3e_a21f, - 0x4284_715b_7ccc_8162, - ]), - v: Fq::from_raw([ - 0xbf09_6275_684b_b8ca, - 0xc7ba_2458_90af_256d, - 0x5911_9f3e_8638_0eb0, - 0x3793_de18_2f9f_b1d2, - ]), - }) - .mul_by_cofactor(); - assert!(p.is_on_curve_vartime()); - - assert_eq!( - (p * Fr::from(1000u64)) * Fr::from(3938u64), - p * (Fr::from(1000u64) * Fr::from(3938u64)), - ); -} - -#[test] -fn test_batch_normalize() { - let mut p = ExtendedPoint::from(AffinePoint { - u: Fq::from_raw([ - 0x81c5_71e5_d883_cfb0, - 0x049f_7a68_6f14_7029, - 0xf539_c860_bc3e_a21f, - 0x4284_715b_7ccc_8162, - ]), - v: Fq::from_raw([ - 0xbf09_6275_684b_b8ca, - 0xc7ba_2458_90af_256d, - 0x5911_9f3e_8638_0eb0, - 0x3793_de18_2f9f_b1d2, - ]), - }) - .mul_by_cofactor(); - - let mut v = vec![]; - for _ in 0..10 { - v.push(p); - p = p.double(); - } - - for p in &v { - assert!(p.is_on_curve_vartime()); - } - - let expected: std::vec::Vec<_> = v.iter().map(|p| AffinePoint::from(*p)).collect(); - let result1: std::vec::Vec<_> = batch_normalize(&mut v).collect(); - for i in 0..10 { - assert!(expected[i] == result1[i]); - assert!(v[i].is_on_curve_vartime()); - assert!(AffinePoint::from(v[i]) == expected[i]); - } - let result2: std::vec::Vec<_> = batch_normalize(&mut v).collect(); - for i in 0..10 { - assert!(expected[i] == result2[i]); - assert!(v[i].is_on_curve_vartime()); - assert!(AffinePoint::from(v[i]) == expected[i]); - } -} - -#[cfg(test)] -const FULL_GENERATOR: AffinePoint = AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xe4b3_d35d_f1a7_adfe, - 0xcaf5_5d1b_29bf_81af, - 0x8b0f_03dd_d60a_8187, - 0x62ed_cbb8_bf37_87c8, - ]), - Fq::from_raw([0xb, 0x0, 0x0, 0x0]), -); - -#[cfg(test)] -const EIGHT_TORSION: [AffinePoint; 8] = [ - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xd92e_6a79_2720_0d43, - 0x7aa4_1ac4_3dae_8582, - 0xeaaa_e086_a166_18d1, - 0x71d4_df38_ba9e_7973, - ]), - Fq::from_raw([ - 0xff0d_2068_eff4_96dd, - 0x9106_ee90_f384_a4a1, - 0x16a1_3035_ad4d_7266, - 0x4958_bdb2_1966_982e, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xfffe_ffff_0000_0001, - 0x67ba_a400_89fb_5bfe, - 0xa5e8_0b39_939e_d334, - 0x73ed_a753_299d_7d47, - ]), - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0xd92e_6a79_2720_0d43, - 0x7aa4_1ac4_3dae_8582, - 0xeaaa_e086_a166_18d1, - 0x71d4_df38_ba9e_7973, - ]), - Fq::from_raw([ - 0x00f2_df96_100b_6924, - 0xc2b6_b572_0c79_b75d, - 0x1c98_a7d2_5c54_659e, - 0x2a94_e9a1_1036_e51a, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - Fq::from_raw([ - 0xffff_ffff_0000_0000, - 0x53bd_a402_fffe_5bfe, - 0x3339_d808_09a1_d805, - 0x73ed_a753_299d_7d48, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0x26d1_9585_d8df_f2be, - 0xd919_893e_c24f_d67c, - 0x488e_f781_683b_bf33, - 0x0218_c81a_6eff_03d4, - ]), - Fq::from_raw([ - 0x00f2_df96_100b_6924, - 0xc2b6_b572_0c79_b75d, - 0x1c98_a7d2_5c54_659e, - 0x2a94_e9a1_1036_e51a, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0x0001_0000_0000_0000, - 0xec03_0002_7603_0000, - 0x8d51_ccce_7603_04d0, - 0x0, - ]), - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([ - 0x26d1_9585_d8df_f2be, - 0xd919_893e_c24f_d67c, - 0x488e_f781_683b_bf33, - 0x0218_c81a_6eff_03d4, - ]), - Fq::from_raw([ - 0xff0d_2068_eff4_96dd, - 0x9106_ee90_f384_a4a1, - 0x16a1_3035_ad4d_7266, - 0x4958_bdb2_1966_982e, - ]), - ), - AffinePoint::from_raw_unchecked( - Fq::from_raw([0x0, 0x0, 0x0, 0x0]), - Fq::from_raw([0x1, 0x0, 0x0, 0x0]), - ), -]; - -#[test] -fn find_eight_torsion() { - let g = ExtendedPoint::from(FULL_GENERATOR); - assert!(!bool::from(g.is_small_order())); - let g = g.multiply(&FR_MODULUS_BYTES); - assert!(bool::from(g.is_small_order())); - - let mut cur = g; - - for (i, point) in EIGHT_TORSION.iter().enumerate() { - let tmp = AffinePoint::from(cur); - if &tmp != point { - panic!("{}th torsion point should be {:?}", i, tmp); - } - - cur += &g; - } -} - -#[test] -fn find_curve_generator() { - let mut trial_bytes = [0; 32]; - for _ in 0..255 { - let a = AffinePoint::from_bytes(trial_bytes); - if bool::from(a.is_some()) { - let a = a.unwrap(); - assert!(a.is_on_curve_vartime()); - let b = ExtendedPoint::from(a); - let b = b.multiply(&FR_MODULUS_BYTES); - assert!(bool::from(b.is_small_order())); - let b = b.double(); - assert!(bool::from(b.is_small_order())); - let b = b.double(); - assert!(bool::from(b.is_small_order())); - if !bool::from(b.is_identity()) { - let b = b.double(); - assert!(bool::from(b.is_small_order())); - assert!(bool::from(b.is_identity())); - assert_eq!(FULL_GENERATOR, a); - assert_eq!(AffinePoint::generator(), a); - assert!(bool::from(a.mul_by_cofactor().is_torsion_free())); - return; - } - } - - trial_bytes[0] += 1; - } - - panic!("should have found a generator of the curve"); -} - -#[test] -fn test_small_order() { - for point in EIGHT_TORSION.iter() { - assert!(bool::from(point.is_small_order())); - } -} - -#[test] -fn test_is_identity() { - let a = EIGHT_TORSION[0].mul_by_cofactor(); - let b = EIGHT_TORSION[1].mul_by_cofactor(); - - assert_eq!(a.u, b.u); - assert_eq!(a.v, a.z); - assert_eq!(b.v, b.z); - assert!(a.v != b.v); - assert!(a.z != b.z); - - assert!(bool::from(a.is_identity())); - assert!(bool::from(b.is_identity())); - - for point in EIGHT_TORSION.iter() { - assert!(bool::from(point.mul_by_cofactor().is_identity())); - } -} - -#[test] -fn test_mul_consistency() { - let a = Fr([ - 0x21e6_1211_d993_4f2e, - 0xa52c_058a_693c_3e07, - 0x9ccb_77bf_b12d_6360, - 0x07df_2470_ec94_398e, - ]); - let b = Fr([ - 0x0333_6d1c_be19_dbe0, - 0x0153_618f_6156_a536, - 0x2604_c9e1_fc3c_6b15, - 0x04ae_581c_eb02_8720, - ]); - let c = Fr([ - 0xd7ab_f5bb_2468_3f4c, - 0x9d77_12cc_274b_7c03, - 0x9732_93db_9683_789f, - 0x0b67_7e29_380a_97a7, - ]); - assert_eq!(a * b, c); - let p = ExtendedPoint::from(AffinePoint { - u: Fq::from_raw([ - 0x81c5_71e5_d883_cfb0, - 0x049f_7a68_6f14_7029, - 0xf539_c860_bc3e_a21f, - 0x4284_715b_7ccc_8162, - ]), - v: Fq::from_raw([ - 0xbf09_6275_684b_b8ca, - 0xc7ba_2458_90af_256d, - 0x5911_9f3e_8638_0eb0, - 0x3793_de18_2f9f_b1d2, - ]), - }) - .mul_by_cofactor(); - assert_eq!(p * c, (p * a) * b); - - // Test Mul implemented on ExtendedNielsPoint - assert_eq!(p * c, (p.to_niels() * a) * b); - assert_eq!(p.to_niels() * c, (p * a) * b); - assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b); - - // Test Mul implemented on AffineNielsPoint - let p_affine_niels = AffinePoint::from(p).to_niels(); - assert_eq!(p * c, (p_affine_niels * a) * b); - assert_eq!(p_affine_niels * c, (p * a) * b); - assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b); -} - -#[test] -fn test_serialization_consistency() { - let gen = FULL_GENERATOR.mul_by_cofactor(); - let mut p = gen; - - let v = vec![ - [ - 203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110, 170, 185, 179, 108, 97, - 63, 13, 211, 247, 120, 79, 219, 110, 234, 131, 123, 19, 215, - ], - [ - 113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222, 233, 195, 203, 195, - 167, 129, 89, 121, 240, 142, 51, 166, 64, 250, 184, 202, 154, 177, - ], - [ - 197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149, 141, 240, 29, 14, 82, - 86, 225, 126, 129, 186, 157, 148, 162, 219, 51, 156, 199, - ], - [ - 182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97, 200, 139, 192, 83, - 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177, 19, 179, 0, - ], - [ - 118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38, 214, 176, 250, 4, - 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177, 230, 185, - ], - [ - 226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82, 174, 113, 112, 166, - 138, 171, 166, 113, 52, 251, 129, 197, 138, 45, 195, 7, 61, 140, - ], - [ - 38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135, 204, 215, 0, 33, - 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207, 12, 16, 24, - ], - [ - 17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85, 59, 86, 3, 226, 211, - 67, 179, 29, 238, 181, 102, 142, 58, 63, 57, 89, 174, 138, - ], - [ - 210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8, 140, 142, 216, 93, - 190, 183, 116, 174, 63, 33, 242, 177, 118, 148, 40, 241, 203, - ], - [ - 0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121, 185, 29, 152, 14, - 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53, 54, 68, - ], - [ - 178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56, 81, 239, 155, 122, - 59, 188, 237, 253, 11, 169, 208, 236, 12, 4, 163, 211, 88, 97, - ], - [ - 246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115, 144, 12, 90, 150, - 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67, 75, 57, 67, - ], - [ - 212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248, 22, 201, 208, 166, - 2, 83, 134, 130, 85, 129, 166, 136, 185, 191, 163, 38, 54, 10, - ], - [ - 8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19, 219, 143, 64, 161, - 99, 199, 77, 39, 148, 70, 213, 246, 227, 150, 178, 237, 178, - ], - [ - 11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84, 157, 214, 167, 73, - 233, 115, 81, 124, 134, 15, 31, 181, 60, 184, 130, 175, 159, - ], - [ - 141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80, 247, 9, 107, 124, 0, - 26, 203, 16, 237, 34, 214, 147, 133, 15, 29, 236, 37, 88, - ], - ]; - - for expected_serialized in v { - assert!(p.is_on_curve_vartime()); - let affine = AffinePoint::from(p); - let serialized = affine.to_bytes(); - let deserialized = AffinePoint::from_bytes(serialized).unwrap(); - assert_eq!(affine, deserialized); - assert_eq!(expected_serialized, serialized); - p += gen; - } -} diff --git a/jubjub/src/util.rs b/jubjub/src/util.rs deleted file mode 100644 index bd25dd06a..000000000 --- a/jubjub/src/util.rs +++ /dev/null @@ -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; - } - } - }; -} diff --git a/jubjub/tests/common.rs b/jubjub/tests/common.rs deleted file mode 100644 index a4535edbe..000000000 --- a/jubjub/tests/common.rs +++ /dev/null @@ -1,29 +0,0 @@ -use jubjub::*; -use rand_core::{RngCore, SeedableRng}; -use rand_xorshift::XorShiftRng; - -pub const NUM_BLACK_BOX_CHECKS: u32 = 2000; - -pub fn new_rng() -> XorShiftRng { - XorShiftRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) -} - -pub trait MyRandom { - fn new_random(rng: &mut T) -> Self; -} - -impl MyRandom for Fq { - fn new_random(rng: &mut T) -> Self { - let mut random_bytes = [0u8; 64]; - rng.fill_bytes(&mut random_bytes); - Fq::from_bytes_wide(&random_bytes) - } -} - -impl MyRandom for Fr { - fn new_random(rng: &mut T) -> Self { - let mut random_bytes = [0u8; 64]; - rng.fill_bytes(&mut random_bytes); - Fr::from_bytes_wide(&random_bytes) - } -} diff --git a/jubjub/tests/fq_blackbox.rs b/jubjub/tests/fq_blackbox.rs deleted file mode 100644 index a823c9b0c..000000000 --- a/jubjub/tests/fq_blackbox.rs +++ /dev/null @@ -1,120 +0,0 @@ -mod common; - -use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; -use jubjub::*; - -#[test] -fn test_to_and_from_bytes() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, Fq::from_bytes(&Fq::to_bytes(&a)).unwrap()); - } -} - -#[test] -fn test_additive_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - let c = Fq::new_random(&mut rng); - assert_eq!((a + b) + c, a + (b + c)) - } -} - -#[test] -fn test_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, a + Fq::zero()); - assert_eq!(a, Fq::zero() + a); - } -} - -#[test] -fn test_subtract_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, a - Fq::zero()); - assert_eq!(a, Fq::zero() - -&a); - } -} - -#[test] -fn test_additive_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let a_neg = -&a; - assert_eq!(Fq::zero(), a + a_neg); - assert_eq!(Fq::zero(), a_neg + a); - } -} - -#[test] -fn test_additive_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - assert_eq!(a + b, b + a); - } -} - -#[test] -fn test_multiplicative_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - let c = Fq::new_random(&mut rng); - assert_eq!((a * b) * c, a * (b * c)) - } -} - -#[test] -fn test_multiplicative_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(a, a * Fq::one()); - assert_eq!(a, Fq::one() * a); - } -} - -#[test] -fn test_multiplicative_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - if a == Fq::zero() { - continue; - } - let a_inv = a.invert().unwrap(); - assert_eq!(Fq::one(), a * a_inv); - assert_eq!(Fq::one(), a_inv * a); - } -} - -#[test] -fn test_multiplicative_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - let b = Fq::new_random(&mut rng); - assert_eq!(a * b, b * a); - } -} - -#[test] -fn test_multiply_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fq::new_random(&mut rng); - assert_eq!(Fq::zero(), Fq::zero() * a); - assert_eq!(Fq::zero(), a * Fq::zero()); - } -} diff --git a/jubjub/tests/fr_blackbox.rs b/jubjub/tests/fr_blackbox.rs deleted file mode 100644 index 6e36d0f85..000000000 --- a/jubjub/tests/fr_blackbox.rs +++ /dev/null @@ -1,120 +0,0 @@ -mod common; - -use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; -use jubjub::*; - -#[test] -fn test_to_and_from_bytes() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, Fr::from_bytes(&Fr::to_bytes(&a)).unwrap()); - } -} - -#[test] -fn test_additive_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - let c = Fr::new_random(&mut rng); - assert_eq!((a + b) + c, a + (b + c)) - } -} - -#[test] -fn test_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, a + Fr::zero()); - assert_eq!(a, Fr::zero() + a); - } -} - -#[test] -fn test_subtract_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, a - Fr::zero()); - assert_eq!(a, Fr::zero() - -&a); - } -} - -#[test] -fn test_additive_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let a_neg = -&a; - assert_eq!(Fr::zero(), a + a_neg); - assert_eq!(Fr::zero(), a_neg + a); - } -} - -#[test] -fn test_additive_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - assert_eq!(a + b, b + a); - } -} - -#[test] -fn test_multiplicative_associativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - let c = Fr::new_random(&mut rng); - assert_eq!((a * b) * c, a * (b * c)) - } -} - -#[test] -fn test_multiplicative_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(a, a * Fr::one()); - assert_eq!(a, Fr::one() * a); - } -} - -#[test] -fn test_multiplicative_inverse() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - if a == Fr::zero() { - continue; - } - let a_inv = a.invert().unwrap(); - assert_eq!(Fr::one(), a * a_inv); - assert_eq!(Fr::one(), a_inv * a); - } -} - -#[test] -fn test_multiplicative_commutativity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - let b = Fr::new_random(&mut rng); - assert_eq!(a * b, b * a); - } -} - -#[test] -fn test_multiply_additive_identity() { - let mut rng = new_rng(); - for _ in 0..NUM_BLACK_BOX_CHECKS { - let a = Fr::new_random(&mut rng); - assert_eq!(Fr::zero(), Fr::zero() * a); - assert_eq!(Fr::zero(), a * Fr::zero()); - } -} diff --git a/pairing/.gitignore b/pairing/.gitignore deleted file mode 100644 index 4308d8220..000000000 --- a/pairing/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -target/ -**/*.rs.bk -Cargo.lock diff --git a/pairing/COPYRIGHT b/pairing/COPYRIGHT deleted file mode 100644 index c3876a4d7..000000000 --- a/pairing/COPYRIGHT +++ /dev/null @@ -1,14 +0,0 @@ -Copyrights in the "pairing" library are retained by their contributors. No -copyright assignment is required to contribute to the "pairing" library. - -The "pairing" library is licensed under either of - - * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) - * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) - -at your option. - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/pairing/Cargo.toml b/pairing/Cargo.toml deleted file mode 100644 index 5993a5887..000000000 --- a/pairing/Cargo.toml +++ /dev/null @@ -1,36 +0,0 @@ -[package] -name = "pairing" - -# Remember to change version string in README.md. -version = "0.17.0" -authors = [ - "Sean Bowe ", - "Jack Grigg ", -] -readme = "README.md" -license = "MIT/Apache-2.0" - -description = "Pairing-friendly elliptic curve library" -documentation = "https://docs.rs/pairing/" -homepage = "https://github.com/ebfull/pairing" -repository = "https://github.com/ebfull/pairing" -edition ="2018" - -[dependencies] -byteorder = "1" -ff = { version = "0.7", path = "../ff", features = ["derive"] } -group = { version = "0.7", path = "../group" } -rand_core = "0.5" -subtle = "2.2.1" - -[dev-dependencies] -criterion = "0.3" -rand_xorshift = "0.2" - -[features] -unstable-features = ["expose-arith"] -expose-arith = [] -default = [] - -[badges] -maintenance = { status = "actively-developed" } diff --git a/pairing/LICENSE-APACHE b/pairing/LICENSE-APACHE deleted file mode 100644 index 16fe87b06..000000000 --- a/pairing/LICENSE-APACHE +++ /dev/null @@ -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. diff --git a/pairing/LICENSE-MIT b/pairing/LICENSE-MIT deleted file mode 100644 index 31aa79387..000000000 --- a/pairing/LICENSE-MIT +++ /dev/null @@ -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. diff --git a/pairing/README.md b/pairing/README.md deleted file mode 100644 index e46fc58c3..000000000 --- a/pairing/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# pairing [![Crates.io](https://img.shields.io/crates/v/pairing.svg)](https://crates.io/crates/pairing) # - -`pairing` is a crate for using pairing-friendly elliptic curves. - -`pairing` provides basic traits for pairing-friendly elliptic curve constructions. -Specific curves are implemented in separate crates: - -- [`bls12_381`](https://crates.io/crates/bls12_381) - the BLS12-381 curve. - -## [Documentation](https://docs.rs/pairing/) - -Bring the `pairing` crate into your project just as you normally would. - -## Security Warnings - -This library does not make any guarantees about constant-time operations, memory -access patterns, or resistance to side-channel attacks. - -## License - -Licensed under either of - - * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or - http://www.apache.org/licenses/LICENSE-2.0) - * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally -submitted for inclusion in the work by you, as defined in the Apache-2.0 -license, shall be dual licensed as above, without any additional terms or -conditions. diff --git a/pairing/src/lib.rs b/pairing/src/lib.rs deleted file mode 100644 index c32d9d485..000000000 --- a/pairing/src/lib.rs +++ /dev/null @@ -1,115 +0,0 @@ -//! A library for working with pairing-friendly curves. - -// `clippy` is a code linting tool for improving code quality by catching -// common mistakes or strange code patterns. If the `cargo-clippy` feature -// is provided, all compiler warnings are prohibited. -#![cfg_attr(feature = "cargo-clippy", deny(warnings))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::inline_always))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::unreadable_literal))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::many_single_char_names))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::new_without_default))] -#![cfg_attr(feature = "cargo-clippy", allow(clippy::write_literal))] -// Catch documentation errors caused by code changes. -#![deny(intra_doc_link_resolution_failure)] -// Force public structures to implement Debug -#![deny(missing_debug_implementations)] - -#[cfg(test)] -pub mod tests; - -use core::ops::Mul; -use ff::PrimeField; -use group::{ - prime::{PrimeCurve, PrimeCurveAffine}, - Group, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, UncompressedEncoding, -}; - -/// An "engine" is a collection of types (fields, elliptic curve groups, etc.) -/// with well-defined relationships. In particular, the G1/G2 curve groups are -/// of prime order `r`, and are equipped with a bilinear pairing function. -pub trait Engine: Sized + 'static + Clone { - /// This is the scalar field of the engine's groups. - type Fr: PrimeField; - - /// The projective representation of an element in G1. - type G1: PrimeCurve - + From - + GroupOps - + GroupOpsOwned - + ScalarMul - + ScalarMulOwned; - - /// The affine representation of an element in G1. - type G1Affine: PairingCurveAffine< - Scalar = Self::Fr, - Curve = Self::G1, - Pair = Self::G2Affine, - PairingResult = Self::Gt, - > + From - + Mul - + for<'a> Mul<&'a Self::Fr, Output = Self::G1>; - - /// The projective representation of an element in G2. - type G2: PrimeCurve - + From - + GroupOps - + GroupOpsOwned - + ScalarMul - + ScalarMulOwned; - - /// The affine representation of an element in G2. - type G2Affine: PairingCurveAffine< - Scalar = Self::Fr, - Curve = Self::G2, - Pair = Self::G1Affine, - PairingResult = Self::Gt, - > + From - + Mul - + for<'a> Mul<&'a Self::Fr, Output = Self::G2>; - - /// The extension field that hosts the target group of the pairing. - type Gt: Group + ScalarMul + ScalarMulOwned; - - /// Invoke the pairing function `G1 x G2 -> Gt` without the use of precomputation and - /// other optimizations. - fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt; -} - -/// Affine representation of an elliptic curve point that can be used -/// to perform pairings. -pub trait PairingCurveAffine: PrimeCurveAffine + UncompressedEncoding { - type Pair: PairingCurveAffine; - type PairingResult: Group; - - /// Perform a pairing - fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; -} - -/// An engine that can compute sums of pairings in an efficient way. -pub trait MultiMillerLoop: Engine { - /// The prepared form of `Self::G2Affine`. - type G2Prepared: Clone + Send + Sync + From; - - /// The type returned by `Engine::miller_loop`. - type Result: MillerLoopResult; - - /// 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).$$ - fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result; -} - -/// 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 -/// [`MillerLoopResult::final_exponentiation`] is called, which is also expensive. -pub trait MillerLoopResult { - /// The extension field that hosts the target group of the pairing. - type Gt: Group; - - /// This performs a "final exponentiation" routine to convert the result of a Miller - /// loop into an element of [`MillerLoopResult::Gt`], so that it can be compared with - /// other elements of `Gt`. - fn final_exponentiation(&self) -> Self::Gt; -} diff --git a/pairing/src/tests/engine.rs b/pairing/src/tests/engine.rs deleted file mode 100644 index 1d0378421..000000000 --- a/pairing/src/tests/engine.rs +++ /dev/null @@ -1,130 +0,0 @@ -use ff::Field; -use group::{prime::PrimeCurveAffine, Curve, Group}; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; -use std::ops::Mul; - -use crate::{Engine, MillerLoopResult, MultiMillerLoop, PairingCurveAffine}; - -pub fn engine_tests() { - 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..10 { - let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine(); - - assert!(a.pairing_with(&b) == b.pairing_with(&a)); - assert!(a.pairing_with(&b) == E::pairing(&a, &b)); - } - - for _ in 0..1000 { - let z1 = E::G1Affine::identity(); - let z2 = E::G2Affine::identity().into(); - - let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine().into(); - let c = E::G1::random(&mut rng).to_affine(); - let d = E::G2::random(&mut rng).to_affine().into(); - - assert_eq!( - E::Gt::identity(), - E::multi_miller_loop(&[(&z1, &b)]).final_exponentiation() - ); - - assert_eq!( - E::Gt::identity(), - E::multi_miller_loop(&[(&a, &z2)]).final_exponentiation() - ); - - assert_eq!( - E::multi_miller_loop(&[(&z1, &b), (&c, &d)]).final_exponentiation(), - E::multi_miller_loop(&[(&a, &z2), (&c, &d)]).final_exponentiation() - ); - - assert_eq!( - E::multi_miller_loop(&[(&a, &b), (&z1, &d)]).final_exponentiation(), - E::multi_miller_loop(&[(&a, &b), (&c, &z2)]).final_exponentiation() - ); - } - - random_bilinearity_tests::(); - random_miller_loop_tests::(); -} - -fn random_miller_loop_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - // Exercise the miller loop for a reduced pairing - for _ in 0..1000 { - let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine(); - - let p2 = E::pairing(&a, &b); - - let a = a; - let b = b.into(); - - let p1 = E::multi_miller_loop(&[(&a, &b)]).final_exponentiation(); - - assert_eq!(p1, p2); - } - - // Exercise a double miller loop - for _ in 0..1000 { - let a = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine(); - let c = E::G1::random(&mut rng).to_affine(); - let d = E::G2::random(&mut rng).to_affine(); - - let ab = E::pairing(&a, &b); - let cd = E::pairing(&c, &d); - - let abcd = ab + &cd; - - let a = a; - let b = b.into(); - let c = c; - let d = d.into(); - - let abcd_with_double_loop = - E::multi_miller_loop(&[(&a, &b), (&c, &d)]).final_exponentiation(); - - assert_eq!(abcd, abcd_with_double_loop); - } -} - -fn random_bilinearity_tests() { - 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 = E::G1::random(&mut rng).to_affine(); - let b = E::G2::random(&mut rng).to_affine(); - - let c = E::Fr::random(&mut rng); - let d = E::Fr::random(&mut rng); - - let ac = (a * &c).to_affine(); - let ad = (a * &d).to_affine(); - let bc = (b * &c).to_affine(); - let bd = (b * &d).to_affine(); - - let acbd = E::pairing(&ac, &bd); - let adbc = E::pairing(&ad, &bc); - - let ab = E::pairing(&a, &b); - let cd = c * &d; - let abcd = Mul::::mul(ab, cd); - - assert_eq!(acbd, adbc); - assert_eq!(acbd, abcd); - } -} diff --git a/pairing/src/tests/field.rs b/pairing/src/tests/field.rs deleted file mode 100644 index eb2c8fee6..000000000 --- a/pairing/src/tests/field.rs +++ /dev/null @@ -1,243 +0,0 @@ -use ff::{Field, PrimeField}; -use rand_core::{RngCore, SeedableRng}; -use rand_xorshift::XorShiftRng; - -pub fn random_sqrt_tests() { - 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..10000 { - let a = F::random(&mut rng); - let b = a.square(); - - let b = b.sqrt().unwrap(); - let negb = b.neg(); - - assert!(a == b || a == negb); - } - - let mut c = F::one(); - for _ in 0..10000 { - let mut b = c.square(); - - b = b.sqrt().unwrap(); - - if b != c { - b = b.neg(); - } - - assert_eq!(b, c); - - c.add_assign(&F::one()); - } -} - -pub fn random_field_tests() { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, - 0xe5, - ]); - - random_multiplication_tests::(&mut rng); - random_addition_tests::(&mut rng); - random_subtraction_tests::(&mut rng); - random_negation_tests::(&mut rng); - random_doubling_tests::(&mut rng); - random_squaring_tests::(&mut rng); - random_inversion_tests::(&mut rng); - random_expansion_tests::(&mut rng); - - assert!(F::zero().is_zero()); - { - let z = F::zero().neg(); - assert!(z.is_zero()); - } - - assert!(bool::from(F::zero().invert().is_none())); - - // Multiplication by zero - { - let mut a = F::random(&mut rng); - a.mul_assign(&F::zero()); - assert!(a.is_zero()); - } - - // Addition by zero - { - let mut a = F::random(&mut rng); - let copy = a; - a.add_assign(&F::zero()); - assert_eq!(a, copy); - } -} - -pub fn from_str_tests() { - { - let a = "84395729384759238745923745892374598234705297301958723458712394587103249587213984572934750213947582345792304758273458972349582734958273495872304598234"; - let b = "38495729084572938457298347502349857029384609283450692834058293405982304598230458230495820394850293845098234059823049582309485203948502938452093482039"; - let c = "3248875134290623212325429203829831876024364170316860259933542844758450336418538569901990710701240661702808867062612075657861768196242274635305077449545396068598317421057721935408562373834079015873933065667961469731886739181625866970316226171512545167081793907058686908697431878454091011239990119126"; - - let mut a = F::from_str(a).unwrap(); - let b = F::from_str(b).unwrap(); - let c = F::from_str(c).unwrap(); - - a.mul_assign(&b); - - assert_eq!(a, c); - } - - { - let mut rng = XorShiftRng::from_seed([ - 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, - 0xbc, 0xe5, - ]); - - for _ in 0..1000 { - let n = rng.next_u64(); - - let a = F::from_str(&format!("{}", n)).unwrap(); - let b = F::from(n); - - assert_eq!(a, b); - } - } - - assert!(F::from_str("").is_none()); - assert!(F::from_str("0").unwrap().is_zero()); - assert!(F::from_str("00").is_none()); - assert!(F::from_str("00000000000").is_none()); -} - -fn random_multiplication_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - let b = F::random(rng); - let c = F::random(rng); - - let mut t0 = a; // (a * b) * c - t0.mul_assign(&b); - t0.mul_assign(&c); - - let mut t1 = a; // (a * c) * b - t1.mul_assign(&c); - t1.mul_assign(&b); - - let mut t2 = b; // (b * c) * a - t2.mul_assign(&c); - t2.mul_assign(&a); - - assert_eq!(t0, t1); - assert_eq!(t1, t2); - } -} - -fn random_addition_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - let b = F::random(rng); - let c = F::random(rng); - - let mut t0 = a; // (a + b) + c - t0.add_assign(&b); - t0.add_assign(&c); - - let mut t1 = a; // (a + c) + b - t1.add_assign(&c); - t1.add_assign(&b); - - let mut t2 = b; // (b + c) + a - t2.add_assign(&c); - t2.add_assign(&a); - - assert_eq!(t0, t1); - assert_eq!(t1, t2); - } -} - -fn random_subtraction_tests(rng: &mut R) { - for _ in 0..10000 { - let b = F::random(rng); - let a = F::random(rng); - - let mut t0 = a; // (a - b) - t0.sub_assign(&b); - - let mut t1 = b; // (b - a) - t1.sub_assign(&a); - - let mut t2 = t0; // (a - b) + (b - a) = 0 - t2.add_assign(&t1); - - assert!(t2.is_zero()); - } -} - -fn random_negation_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - let mut b = a.neg(); - b.add_assign(&a); - - assert!(b.is_zero()); - } -} - -fn random_doubling_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - assert_eq!(a + a, a.double()); - } -} - -fn random_squaring_tests(rng: &mut R) { - for _ in 0..10000 { - let a = F::random(rng); - assert_eq!(a * a, a.square()); - } -} - -fn random_inversion_tests(rng: &mut R) { - assert!(bool::from(F::zero().invert().is_none())); - - for _ in 0..10000 { - let mut a = F::random(rng); - let b = a.invert().unwrap(); // probablistically nonzero - a.mul_assign(&b); - - assert_eq!(a, F::one()); - } -} - -fn random_expansion_tests(rng: &mut R) { - for _ in 0..10000 { - // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) - - let a = F::random(rng); - let b = F::random(rng); - let c = F::random(rng); - let d = F::random(rng); - - let mut t0 = a; - t0.add_assign(&b); - let mut t1 = c; - t1.add_assign(&d); - t0.mul_assign(&t1); - - let mut t2 = a; - t2.mul_assign(&c); - let mut t3 = b; - t3.mul_assign(&c); - let mut t4 = a; - t4.mul_assign(&d); - let mut t5 = b; - t5.mul_assign(&d); - - t2.add_assign(&t3); - t2.add_assign(&t4); - t2.add_assign(&t5); - - assert_eq!(t0, t2); - } -} diff --git a/pairing/src/tests/mod.rs b/pairing/src/tests/mod.rs deleted file mode 100644 index d6ad6a12f..000000000 --- a/pairing/src/tests/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -pub mod engine; -pub mod field; -pub mod repr; diff --git a/pairing/src/tests/repr.rs b/pairing/src/tests/repr.rs deleted file mode 100644 index bdaffaa83..000000000 --- a/pairing/src/tests/repr.rs +++ /dev/null @@ -1,23 +0,0 @@ -use ff::PrimeField; -use rand_core::SeedableRng; -use rand_xorshift::XorShiftRng; - -pub fn random_repr_tests() { - random_encoding_tests::

(); -} - -fn random_encoding_tests() { - 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 = P::random(&mut rng); - - let v = r.to_repr(); - let rdecoded = P::from_repr(v).unwrap(); - - assert_eq!(r, rdecoded); - } -} diff --git a/zcash_client_backend/Cargo.toml b/zcash_client_backend/Cargo.toml index 140d6d57b..a4ce4290e 100644 --- a/zcash_client_backend/Cargo.toml +++ b/zcash_client_backend/Cargo.toml @@ -13,12 +13,12 @@ edition = "2018" [dependencies] bech32 = "0.7" -bls12_381 = { version = "0.2", path = "../bls12_381" } +bls12_381 = "0.2" bs58 = { version = "0.3", features = ["check"] } -ff = { version = "0.7", path = "../ff" } -group = { version = "0.7", path = "../group" } +ff = "0.7" +group = "0.7" hex = "0.4" -jubjub = { version = "0.4", path = "../jubjub" } +jubjub = "0.4" protobuf = "=2.14.0" # 2.15 has MSRV of 1.44.1 subtle = "2" zcash_primitives = { version = "0.3", path = "../zcash_primitives" } diff --git a/zcash_client_sqlite/Cargo.toml b/zcash_client_sqlite/Cargo.toml index 32b5db3ca..d0979aa79 100644 --- a/zcash_client_sqlite/Cargo.toml +++ b/zcash_client_sqlite/Cargo.toml @@ -14,9 +14,9 @@ edition = "2018" [dependencies] bech32 = "0.7" bs58 = { version = "0.3", features = ["check"] } -ff = { version = "0.7", path = "../ff" } -group = { version = "0.7", path = "../group" } -jubjub = { version = "0.4", path = "../jubjub" } +ff = "0.7" +group = "0.7" +jubjub = "0.4" protobuf = "2" rand_core = "0.5.1" rusqlite = { version = "0.23", features = ["bundled"] } diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index c60d94f32..393ab55cd 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -18,15 +18,15 @@ all-features = true aes = "0.5" blake2b_simd = "0.5" blake2s_simd = "0.5" -bls12_381 = { version = "0.2", path = "../bls12_381" } +bls12_381 = "0.2" byteorder = "1" crypto_api_chachapoly = "0.4" equihash = { version = "0.1", path = "../components/equihash" } -ff = { version = "0.7", path = "../ff" } +ff = "0.7" fpe = "0.3" -group = { version = "0.7", path = "../group" } +group = "0.7" hex = "0.4" -jubjub = { version = "0.4", path = "../jubjub" } +jubjub = "0.4" lazy_static = "1" log = "0.4" rand = "0.7" diff --git a/zcash_proofs/Cargo.toml b/zcash_proofs/Cargo.toml index 6dc1bbdd9..b1b83b768 100644 --- a/zcash_proofs/Cargo.toml +++ b/zcash_proofs/Cargo.toml @@ -15,14 +15,14 @@ edition = "2018" all-features = true [dependencies] -bellman = { version = "0.7", path = "../bellman", default-features = false, features = ["groth16"] } +bellman = { version = "0.7", default-features = false, features = ["groth16"] } blake2b_simd = "0.5" -bls12_381 = { version = "0.2", path = "../bls12_381" } +bls12_381 = "0.2" byteorder = "1" directories = { version = "3", optional = true } -ff = { version = "0.7", path = "../ff" } -group = { version = "0.7", path = "../group" } -jubjub = { version = "0.4", path = "../jubjub" } +ff = "0.7" +group = "0.7" +jubjub = "0.4" lazy_static = "1" minreq = { version = "2", features = ["https"], optional = true } rand_core = "0.5.1"