├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── COPYRIGHT ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── rust-toolchain.toml └── src ├── cofactor.rs ├── lib.rs ├── prime.rs ├── tests └── mod.rs └── wnaf.rs /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI checks 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: main 7 | 8 | jobs: 9 | test-msrv: 10 | name: Test MSRV on ${{ matrix.os }} 11 | runs-on: ${{ matrix.os }} 12 | strategy: 13 | matrix: 14 | os: [ubuntu-latest, windows-latest, macOS-latest] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - name: Run tests 18 | run: cargo test --verbose --all-features 19 | - name: Verify working directory is clean 20 | run: git diff --exit-code 21 | 22 | test-latest: 23 | name: Test latest on ${{ matrix.os }} 24 | runs-on: ${{ matrix.os }} 25 | strategy: 26 | matrix: 27 | os: [ubuntu-latest, windows-latest, macOS-latest] 28 | steps: 29 | - uses: actions/checkout@v4 30 | - uses: dtolnay/rust-toolchain@stable 31 | id: toolchain 32 | - run: rustup override set ${{steps.toolchain.outputs.name}} 33 | - name: Remove lockfile to build with latest dependencies 34 | run: rm Cargo.lock 35 | - name: Run tests 36 | run: cargo test --verbose --all-features 37 | - name: Verify working directory is clean (excluding lockfile) 38 | run: git diff --exit-code ':!Cargo.lock' 39 | 40 | build-nodefault: 41 | name: Build target ${{ matrix.target }} 42 | runs-on: ubuntu-latest 43 | strategy: 44 | matrix: 45 | target: 46 | - wasm32-wasi 47 | - thumbv6m-none-eabi 48 | - thumbv7em-none-eabihf 49 | steps: 50 | - uses: actions/checkout@v4 51 | with: 52 | path: crate_root 53 | # We use a synthetic crate to ensure no dev-dependencies are enabled, which can 54 | # be incompatible with some of these targets. 55 | - name: Create synthetic crate for testing 56 | run: cargo init --edition 2021 --lib ci-build 57 | - name: Copy Rust version into synthetic crate 58 | run: cp crate_root/rust-toolchain.toml ci-build/ 59 | - name: Copy patch directives into synthetic crate 60 | run: | 61 | echo "[patch.crates-io]" >> ./ci-build/Cargo.toml 62 | cat ./crate_root/Cargo.toml | sed "0,/.\+\(patch.crates.\+\)/d" >> ./ci-build/Cargo.toml 63 | - name: Add no_std pragma to lib.rs 64 | run: | 65 | echo "#![no_std]" > ./ci-build/src/lib.rs 66 | - name: Add group as a dependency of the synthetic crate 67 | working-directory: ./ci-build 68 | # run: cargo add --no-default-features --path ../crate_root 69 | run: sed -i 's;\[dependencies\];\[dependencies\]\ngroup = { path = "../crate_root", default-features = false };g' ./Cargo.toml 70 | - name: Add target 71 | working-directory: ./ci-build 72 | run: rustup target add ${{ matrix.target }} 73 | - name: Build for target 74 | working-directory: ./ci-build 75 | run: cargo build --verbose --target ${{ matrix.target }} 76 | 77 | doc-links: 78 | name: Intra-doc links 79 | runs-on: ubuntu-latest 80 | steps: 81 | - uses: actions/checkout@v4 82 | - run: cargo fetch 83 | # Requires #![deny(rustdoc::broken_intra_doc_links)] in crates. 84 | - run: sudo apt-get -y install libfontconfig1-dev 85 | - name: Check intra-doc links 86 | run: cargo doc --all-features --document-private-items 87 | 88 | fmt: 89 | name: Rustfmt 90 | runs-on: ubuntu-latest 91 | steps: 92 | - uses: actions/checkout@v4 93 | - run: cargo fmt -- --check 94 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this library will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this library adheres to Rust's notion of 6 | [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | 10 | ## [0.13.0] - 2022-12-06 11 | ### Changed 12 | - Bumped `ff` to `0.13` 13 | 14 | ## [0.12.1] - 2022-10-13 15 | ### Added 16 | - `group::{WnafBase, WnafScalar}` structs for caching precomputations of both 17 | bases and scalars, for improved many-base many-scalar multiplication 18 | performance. 19 | - `impl memuse::DynamicUsage for group::{Wnaf WnafBase, WnafScalar}`, behind the 20 | new `wnaf-memuse` feature flag, to enable the heap usage of these types to be 21 | measured at runtime. 22 | 23 | ### Changed 24 | - Removed temporary allocations from `Wnaf` internals for improved performance. 25 | 26 | ## [0.12.0] - 2022-05-04 27 | ### Changed 28 | - MSRV is now 1.56.0. 29 | - Bumped `ff` to `0.12` 30 | 31 | ## [0.11.0] - 2021-09-02 32 | ### Fixed 33 | - The affine scalar multiplication bounds on the following traits had typos that 34 | prevented multiplying by `&Self::Scalar`, which has now been fixed: 35 | - `group::cofactor::{CofactorCurve::Affine, CofactorCurveAffine}` 36 | - `group::prime::{PrimeCurve::Affine, PrimeCurveAffine}` 37 | 38 | ### Added 39 | - `Copy + Send + Sync + 'static` bounds on `group::GroupEncoding::Repr`. 40 | 41 | ### Changed 42 | - Bumped `ff` to 0.11. 43 | 44 | ## [0.10.0] - 2021-06-01 45 | ### Added 46 | - `group::ff`, which re-exports the `ff` crate to make version-matching easier. 47 | 48 | ### Changed 49 | - MSRV is now 1.51.0. 50 | - Bumped `ff` to 0.10. 51 | 52 | ### Removed 53 | - `group::cofactor::CofactorGroup::is_torsion_free` provided implementation 54 | (trait implementors must now implement this method themselves). This avoids 55 | a hard dependency on the `ff/bits` feature flag. 56 | 57 | ## [0.9.0] - 2021-01-06 58 | ### Changed 59 | - Bumped dependencies to `ff 0.9`, `rand_core 0.6`, `rand 0.8`. 60 | 61 | ## [0.8.0] - 2020-09-08 62 | ### Added 63 | - `no_std` support. 64 | 65 | ### Changed 66 | - MSRV is now 1.44.0. 67 | - Bumped `ff` to 0.8. 68 | - `group::{wnaf, Wnaf, WnafGroup}` are now gated behind the (default-enabled) 69 | `alloc` feature flag. The `byteorder` dependency is now optional. 70 | - `group::tests` is now gated behind the `tests` feature flag. The `rand` and 71 | `rand_xorshift` dependencies are now optional. 72 | 73 | ### Removed 74 | - `fmt::Display` bound from the following traits: 75 | - `group::Group` 76 | - `group::cofactor::CofactorCurveAffine` 77 | - `group::prime::PrimeCurveAffine` 78 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyrights in the "group" library are retained by their contributors. No 2 | copyright assignment is required to contribute to the "group" library. 3 | 4 | The "group" library is licensed under either of 5 | 6 | * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 7 | * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) 8 | 9 | at your option. 10 | 11 | Unless you explicitly state otherwise, any contribution intentionally 12 | submitted for inclusion in the work by you, as defined in the Apache-2.0 13 | license, shall be dual licensed as above, without any additional terms or 14 | conditions. 15 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "ff" 7 | version = "0.13.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" 10 | dependencies = [ 11 | "rand_core", 12 | "subtle", 13 | ] 14 | 15 | [[package]] 16 | name = "group" 17 | version = "0.13.0" 18 | dependencies = [ 19 | "ff", 20 | "memuse", 21 | "rand", 22 | "rand_core", 23 | "rand_xorshift", 24 | "subtle", 25 | ] 26 | 27 | [[package]] 28 | name = "memuse" 29 | version = "0.2.1" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "2145869435ace5ea6ea3d35f59be559317ec9a0d04e1812d5f185a87b6d36f1a" 32 | 33 | [[package]] 34 | name = "rand" 35 | version = "0.8.5" 36 | source = "registry+https://github.com/rust-lang/crates.io-index" 37 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 38 | dependencies = [ 39 | "rand_core", 40 | ] 41 | 42 | [[package]] 43 | name = "rand_core" 44 | version = "0.6.4" 45 | source = "registry+https://github.com/rust-lang/crates.io-index" 46 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 47 | 48 | [[package]] 49 | name = "rand_xorshift" 50 | version = "0.3.0" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" 53 | dependencies = [ 54 | "rand_core", 55 | ] 56 | 57 | [[package]] 58 | name = "subtle" 59 | version = "2.4.1" 60 | source = "registry+https://github.com/rust-lang/crates.io-index" 61 | checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" 62 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "group" 3 | version = "0.13.0" 4 | authors = [ 5 | "Sean Bowe ", 6 | "Jack Grigg ", 7 | ] 8 | edition = "2021" 9 | rust-version = "1.56" 10 | readme = "README.md" 11 | license = "MIT/Apache-2.0" 12 | 13 | description = "Elliptic curve group traits and utilities" 14 | documentation = "https://docs.rs/group/" 15 | homepage = "https://github.com/zkcrypto/group" 16 | repository = "https://github.com/zkcrypto/group" 17 | 18 | [dependencies] 19 | ff = { version = "0.13", default-features = false } 20 | rand = { version = "0.8", optional = true, default-features = false } 21 | rand_core = { version = "0.6", default-features = false } 22 | rand_xorshift = { version = "0.3", optional = true } 23 | subtle = { version = "2.2.1", default-features = false } 24 | 25 | # Crate for exposing the dynamic memory usage of the w-NAF structs. 26 | memuse = { version = "0.2", optional = true } 27 | 28 | [features] 29 | default = ["alloc"] 30 | alloc = [] 31 | tests = ["alloc", "rand", "rand_xorshift"] 32 | wnaf-memuse = ["alloc", "memuse"] 33 | 34 | [badges] 35 | maintenance = { status = "actively-developed" } 36 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # group [![Crates.io](https://img.shields.io/crates/v/group.svg)](https://crates.io/crates/group) # 2 | 3 | `group` is a crate for working with groups over elliptic curves. 4 | 5 | ## RFC process 6 | 7 | This crate follows the [zkcrypto RFC process](https://zkcrypto.github.io/rfcs/). 8 | If you want to propose "substantial" changes to this crate (in particular to the 9 | `group` traits), please [create an RFC](https://github.com/zkcrypto/rfcs) for 10 | wider discussion. 11 | 12 | ## Minimum Supported Rust Version 13 | 14 | Requires Rust **1.56** or higher. 15 | 16 | Minimum supported Rust version can be changed in the future, but it will be done with a 17 | minor version bump. 18 | 19 | ## License 20 | 21 | Licensed under either of 22 | 23 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 24 | http://www.apache.org/licenses/LICENSE-2.0) 25 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 26 | 27 | at your option. 28 | 29 | ### Contribution 30 | 31 | Unless you explicitly state otherwise, any contribution intentionally 32 | submitted for inclusion in the work by you, as defined in the Apache-2.0 33 | license, shall be dual licensed as above, without any additional terms or 34 | conditions. 35 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.56.0" 3 | components = [ "clippy", "rustfmt" ] 4 | -------------------------------------------------------------------------------- /src/cofactor.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::ops::{Mul, Neg}; 3 | use ff::PrimeField; 4 | use subtle::{Choice, CtOption}; 5 | 6 | use crate::{prime::PrimeGroup, Curve, Group, GroupEncoding, GroupOps, GroupOpsOwned}; 7 | 8 | /// This trait represents an element of a cryptographic group with a large prime-order 9 | /// subgroup and a comparatively-small cofactor. 10 | pub trait CofactorGroup: 11 | Group 12 | + GroupEncoding 13 | + GroupOps<::Subgroup> 14 | + GroupOpsOwned<::Subgroup> 15 | { 16 | /// The large prime-order subgroup in which cryptographic operations are performed. 17 | /// If `Self` implements `PrimeGroup`, then `Self::Subgroup` may be `Self`. 18 | type Subgroup: PrimeGroup + Into; 19 | 20 | /// Maps `self` to the prime-order subgroup by multiplying this element by some 21 | /// `k`-multiple of the cofactor. 22 | /// 23 | /// The value `k` does not vary between inputs for a given implementation, but may 24 | /// vary between different implementations of `CofactorGroup` because some groups have 25 | /// more efficient methods of clearing the cofactor when `k` is allowed to be 26 | /// different than `1`. 27 | /// 28 | /// If `Self` implements [`PrimeGroup`], this returns `self`. 29 | fn clear_cofactor(&self) -> Self::Subgroup; 30 | 31 | /// Returns `self` if it is contained in the prime-order subgroup. 32 | /// 33 | /// If `Self` implements [`PrimeGroup`], this returns `Some(self)`. 34 | fn into_subgroup(self) -> CtOption; 35 | 36 | /// Determines if this element is of small order. 37 | /// 38 | /// Returns: 39 | /// - `true` if `self` is in the torsion subgroup. 40 | /// - `false` if `self` is not in the torsion subgroup. 41 | fn is_small_order(&self) -> Choice { 42 | self.clear_cofactor().is_identity() 43 | } 44 | 45 | /// Determines if this element is "torsion free", i.e., is contained in the 46 | /// prime-order subgroup. 47 | /// 48 | /// Returns: 49 | /// - `true` if `self` has trivial torsion and is in the prime-order subgroup. 50 | /// - `false` if `self` has non-zero torsion component and is not in the prime-order 51 | /// subgroup. 52 | fn is_torsion_free(&self) -> Choice; 53 | } 54 | 55 | /// Efficient representation of an elliptic curve point guaranteed to be 56 | /// in the correct prime order subgroup. 57 | pub trait CofactorCurve: 58 | Curve::Affine> + CofactorGroup 59 | { 60 | type Affine: CofactorCurveAffine 61 | + Mul 62 | + for<'r> Mul<&'r Self::Scalar, Output = Self>; 63 | } 64 | 65 | /// Affine representation of an elliptic curve point guaranteed to be 66 | /// in the correct prime order subgroup. 67 | pub trait CofactorCurveAffine: 68 | GroupEncoding 69 | + Copy 70 | + Clone 71 | + Sized 72 | + Send 73 | + Sync 74 | + fmt::Debug 75 | + PartialEq 76 | + Eq 77 | + 'static 78 | + Neg 79 | + Mul<::Scalar, Output = ::Curve> 80 | + for<'r> Mul< 81 | &'r ::Scalar, 82 | Output = ::Curve, 83 | > 84 | { 85 | type Scalar: PrimeField; 86 | type Curve: CofactorCurve; 87 | 88 | /// Returns the additive identity. 89 | fn identity() -> Self; 90 | 91 | /// Returns a fixed generator of unknown exponent. 92 | fn generator() -> Self; 93 | 94 | /// Determines if this point represents the point at infinity; the 95 | /// additive identity. 96 | fn is_identity(&self) -> Choice; 97 | 98 | /// Converts this element to its curve representation. 99 | fn to_curve(&self) -> Self::Curve; 100 | } 101 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | // Catch documentation errors caused by code changes. 3 | #![deny(rustdoc::broken_intra_doc_links)] 4 | 5 | #[cfg(feature = "alloc")] 6 | #[macro_use] 7 | extern crate alloc; 8 | 9 | // Re-export ff to make version-matching easier. 10 | pub use ff; 11 | 12 | use core::fmt; 13 | use core::iter::Sum; 14 | use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 15 | use ff::PrimeField; 16 | use rand_core::RngCore; 17 | use subtle::{Choice, CtOption}; 18 | 19 | pub mod cofactor; 20 | pub mod prime; 21 | #[cfg(feature = "tests")] 22 | pub mod tests; 23 | 24 | #[cfg(feature = "alloc")] 25 | mod wnaf; 26 | #[cfg(feature = "alloc")] 27 | pub use self::wnaf::{Wnaf, WnafBase, WnafGroup, WnafScalar}; 28 | 29 | /// A helper trait for types with a group operation. 30 | pub trait GroupOps: 31 | Add + Sub + AddAssign + SubAssign 32 | { 33 | } 34 | 35 | impl GroupOps for T where 36 | T: Add + Sub + AddAssign + SubAssign 37 | { 38 | } 39 | 40 | /// A helper trait for references with a group operation. 41 | pub trait GroupOpsOwned: for<'r> GroupOps<&'r Rhs, Output> {} 42 | impl GroupOpsOwned for T where T: for<'r> GroupOps<&'r Rhs, Output> {} 43 | 44 | /// A helper trait for types implementing group scalar multiplication. 45 | pub trait ScalarMul: Mul + MulAssign {} 46 | 47 | impl ScalarMul for T where T: Mul + MulAssign 48 | {} 49 | 50 | /// A helper trait for references implementing group scalar multiplication. 51 | pub trait ScalarMulOwned: for<'r> ScalarMul<&'r Rhs, Output> {} 52 | impl ScalarMulOwned for T where T: for<'r> ScalarMul<&'r Rhs, Output> {} 53 | 54 | /// This trait represents an element of a cryptographic group. 55 | pub trait Group: 56 | Clone 57 | + Copy 58 | + fmt::Debug 59 | + Eq 60 | + Sized 61 | + Send 62 | + Sync 63 | + 'static 64 | + Sum 65 | + for<'a> Sum<&'a Self> 66 | + Neg 67 | + GroupOps 68 | + GroupOpsOwned 69 | + ScalarMul<::Scalar> 70 | + ScalarMulOwned<::Scalar> 71 | { 72 | /// Scalars modulo the order of this group's scalar field. 73 | type Scalar: PrimeField; 74 | 75 | /// Returns an element chosen uniformly at random from the non-identity elements of 76 | /// this group. 77 | /// 78 | /// This function is non-deterministic, and samples from the user-provided RNG. 79 | fn random(rng: impl RngCore) -> Self; 80 | 81 | /// Returns the additive identity, also known as the "neutral element". 82 | fn identity() -> Self; 83 | 84 | /// Returns a fixed generator of the prime-order subgroup. 85 | fn generator() -> Self; 86 | 87 | /// Determines if this point is the identity. 88 | fn is_identity(&self) -> Choice; 89 | 90 | /// Doubles this element. 91 | #[must_use] 92 | fn double(&self) -> Self; 93 | } 94 | 95 | /// Efficient representation of an elliptic curve point guaranteed. 96 | pub trait Curve: 97 | Group + GroupOps<::AffineRepr> + GroupOpsOwned<::AffineRepr> 98 | { 99 | /// The affine representation for this elliptic curve. 100 | type AffineRepr; 101 | 102 | /// Converts a batch of projective elements into affine elements. This function will 103 | /// panic if `p.len() != q.len()`. 104 | fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { 105 | assert_eq!(p.len(), q.len()); 106 | 107 | for (p, q) in p.iter().zip(q.iter_mut()) { 108 | *q = p.to_affine(); 109 | } 110 | } 111 | 112 | /// Converts this element into its affine representation. 113 | fn to_affine(&self) -> Self::AffineRepr; 114 | } 115 | 116 | pub trait GroupEncoding: Sized { 117 | /// The encoding of group elements. 118 | /// 119 | /// The `Default` implementation is not required to return a valid point encoding. The 120 | /// bound is present to enable encodings to be constructed generically: 121 | /// ``` 122 | /// # use group::GroupEncoding; 123 | /// # use subtle::CtOption; 124 | /// # struct G; 125 | /// # impl GroupEncoding for G { 126 | /// # type Repr = [u8; 0]; 127 | /// # fn from_bytes(bytes: &Self::Repr) -> CtOption { unimplemented!() } 128 | /// # fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { unimplemented!() } 129 | /// # fn to_bytes(&self) -> Self::Repr { unimplemented!() } 130 | /// # } 131 | /// # let buf = &[0u8; 0][..]; 132 | /// let mut encoding = ::Repr::default(); 133 | /// encoding.as_mut().copy_from_slice(buf); 134 | /// ``` 135 | /// 136 | /// It is recommended that the default should be the all-zeroes encoding. 137 | type Repr: Copy + Default + Send + Sync + 'static + AsRef<[u8]> + AsMut<[u8]>; 138 | 139 | /// Attempts to deserialize a group element from its encoding. 140 | fn from_bytes(bytes: &Self::Repr) -> CtOption; 141 | 142 | /// Attempts to deserialize a group element, not checking if the element is valid. 143 | /// 144 | /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, 145 | /// API invariants may be broken.** Please consider using 146 | /// [`GroupEncoding::from_bytes`] instead. 147 | fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption; 148 | 149 | /// Converts this element into its byte encoding. This may or may not support 150 | /// encoding the identity. 151 | // TODO: Figure out how to handle identity encoding generically. 152 | fn to_bytes(&self) -> Self::Repr; 153 | } 154 | 155 | /// Affine representation of a point on an elliptic curve that has a defined uncompressed 156 | /// encoding. 157 | pub trait UncompressedEncoding: Sized { 158 | type Uncompressed: Default + AsRef<[u8]> + AsMut<[u8]>; 159 | 160 | /// Attempts to deserialize an element from its uncompressed encoding. 161 | fn from_uncompressed(bytes: &Self::Uncompressed) -> CtOption; 162 | 163 | /// Attempts to deserialize an uncompressed element, not checking if the element is in 164 | /// the correct subgroup. 165 | /// 166 | /// **This is dangerous to call unless you trust the bytes you are reading; otherwise, 167 | /// API invariants may be broken.** Please consider using 168 | /// [`UncompressedEncoding::from_uncompressed`] instead. 169 | fn from_uncompressed_unchecked(bytes: &Self::Uncompressed) -> CtOption; 170 | 171 | /// Converts this element into its uncompressed encoding, so long as it's not 172 | /// the point at infinity. 173 | fn to_uncompressed(&self) -> Self::Uncompressed; 174 | } 175 | -------------------------------------------------------------------------------- /src/prime.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::ops::{Mul, Neg}; 3 | use ff::PrimeField; 4 | use subtle::Choice; 5 | 6 | use crate::{Curve, Group, GroupEncoding}; 7 | 8 | /// This trait represents an element of a prime-order cryptographic group. 9 | pub trait PrimeGroup: Group + GroupEncoding {} 10 | 11 | /// Efficient representation of an elliptic curve point guaranteed to be 12 | /// in the correct prime order subgroup. 13 | pub trait PrimeCurve: Curve::Affine> + PrimeGroup { 14 | type Affine: PrimeCurveAffine 15 | + Mul 16 | + for<'r> Mul<&'r Self::Scalar, Output = Self>; 17 | } 18 | 19 | /// Affine representation of an elliptic curve point guaranteed to be 20 | /// in the correct prime order subgroup. 21 | pub trait PrimeCurveAffine: GroupEncoding 22 | + Copy 23 | + Clone 24 | + Sized 25 | + Send 26 | + Sync 27 | + fmt::Debug 28 | + PartialEq 29 | + Eq 30 | + 'static 31 | + Neg 32 | + Mul<::Scalar, Output = ::Curve> 33 | + for<'r> Mul<&'r ::Scalar, Output = ::Curve> 34 | { 35 | type Scalar: PrimeField; 36 | type Curve: PrimeCurve; 37 | 38 | /// Returns the additive identity. 39 | fn identity() -> Self; 40 | 41 | /// Returns a fixed generator of unknown exponent. 42 | fn generator() -> Self; 43 | 44 | /// Determines if this point represents the point at infinity; the 45 | /// additive identity. 46 | fn is_identity(&self) -> Choice; 47 | 48 | /// Converts this element to its curve representation. 49 | fn to_curve(&self) -> Self::Curve; 50 | } 51 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::ops::{Mul, Neg}; 3 | use ff::{Field, PrimeField}; 4 | use rand::SeedableRng; 5 | use rand_xorshift::XorShiftRng; 6 | 7 | use crate::{ 8 | prime::{PrimeCurve, PrimeCurveAffine}, 9 | wnaf::WnafGroup, 10 | GroupEncoding, UncompressedEncoding, 11 | }; 12 | 13 | pub fn curve_tests() { 14 | let mut rng = XorShiftRng::from_seed([ 15 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 16 | 0xe5, 17 | ]); 18 | 19 | // Negation edge case with identity. 20 | { 21 | let z = G::identity().neg(); 22 | assert!(bool::from(z.is_identity())); 23 | } 24 | 25 | // Doubling edge case with identity. 26 | { 27 | let z = G::identity().double(); 28 | assert!(bool::from(z.is_identity())); 29 | } 30 | 31 | // Addition edge cases with identity 32 | { 33 | let mut r = G::random(&mut rng); 34 | let rcopy = r; 35 | r.add_assign(&G::identity()); 36 | assert_eq!(r, rcopy); 37 | r.add_assign(&G::Affine::identity()); 38 | assert_eq!(r, rcopy); 39 | 40 | let mut z = G::identity(); 41 | z.add_assign(&G::identity()); 42 | assert!(bool::from(z.is_identity())); 43 | z.add_assign(&G::Affine::identity()); 44 | assert!(bool::from(z.is_identity())); 45 | 46 | let mut z2 = z; 47 | z2.add_assign(&r); 48 | 49 | z.add_assign(&r.to_affine()); 50 | 51 | assert_eq!(z, z2); 52 | assert_eq!(z, r); 53 | } 54 | 55 | // Transformations 56 | { 57 | let a = G::random(&mut rng); 58 | let b = a.to_affine().to_curve(); 59 | let c = a.to_affine().to_curve().to_affine().to_curve(); 60 | assert_eq!(a, b); 61 | assert_eq!(b, c); 62 | } 63 | 64 | random_addition_tests::(); 65 | random_multiplication_tests::(); 66 | random_doubling_tests::(); 67 | random_negation_tests::(); 68 | random_transformation_tests::(); 69 | random_compressed_encoding_tests::(); 70 | } 71 | 72 | pub fn random_wnaf_tests() { 73 | use crate::wnaf::*; 74 | 75 | let mut rng = XorShiftRng::from_seed([ 76 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 77 | 0xe5, 78 | ]); 79 | 80 | { 81 | let mut table = vec![]; 82 | let mut wnaf = vec![]; 83 | 84 | for w in 2..14 { 85 | for _ in 0..100 { 86 | let g = G::random(&mut rng); 87 | let s = G::Scalar::random(&mut rng); 88 | let mut g1 = g; 89 | g1.mul_assign(s); 90 | 91 | wnaf_table(&mut table, g, w); 92 | wnaf_form(&mut wnaf, s.to_repr(), w); 93 | let g2 = wnaf_exp(&table, &wnaf); 94 | 95 | assert_eq!(g1, g2); 96 | } 97 | } 98 | } 99 | 100 | { 101 | fn only_compiles_if_send(_: &S) {} 102 | 103 | for _ in 0..100 { 104 | let g = G::random(&mut rng); 105 | let s = G::Scalar::random(&mut rng); 106 | let mut g1 = g; 107 | g1.mul_assign(s); 108 | 109 | let g2 = { 110 | let mut wnaf = Wnaf::new(); 111 | wnaf.base(g, 1).scalar(&s) 112 | }; 113 | let g3 = { 114 | let mut wnaf = Wnaf::new(); 115 | wnaf.scalar(&s).base(g) 116 | }; 117 | let g4 = { 118 | let mut wnaf = Wnaf::new(); 119 | let mut shared = wnaf.base(g, 1).shared(); 120 | 121 | only_compiles_if_send(&shared); 122 | 123 | shared.scalar(&s) 124 | }; 125 | let g5 = { 126 | let mut wnaf = Wnaf::new(); 127 | let mut shared = wnaf.scalar(&s).shared(); 128 | 129 | only_compiles_if_send(&shared); 130 | 131 | shared.base(g) 132 | }; 133 | 134 | let g6 = { 135 | let mut wnaf = Wnaf::new(); 136 | { 137 | // Populate the vectors. 138 | wnaf.base(G::random(&mut rng), 1) 139 | .scalar(&G::Scalar::random(&mut rng)); 140 | } 141 | wnaf.base(g, 1).scalar(&s) 142 | }; 143 | let g7 = { 144 | let mut wnaf = Wnaf::new(); 145 | { 146 | // Populate the vectors. 147 | wnaf.base(G::random(&mut rng), 1) 148 | .scalar(&G::Scalar::random(&mut rng)); 149 | } 150 | wnaf.scalar(&s).base(g) 151 | }; 152 | let g8 = { 153 | let mut wnaf = Wnaf::new(); 154 | { 155 | // Populate the vectors. 156 | wnaf.base(G::random(&mut rng), 1) 157 | .scalar(&G::Scalar::random(&mut rng)); 158 | } 159 | let mut shared = wnaf.base(g, 1).shared(); 160 | 161 | only_compiles_if_send(&shared); 162 | 163 | shared.scalar(&s) 164 | }; 165 | let g9 = { 166 | let mut wnaf = Wnaf::new(); 167 | { 168 | // Populate the vectors. 169 | wnaf.base(G::random(&mut rng), 1) 170 | .scalar(&G::Scalar::random(&mut rng)); 171 | } 172 | let mut shared = wnaf.scalar(&s).shared(); 173 | 174 | only_compiles_if_send(&shared); 175 | 176 | shared.base(g) 177 | }; 178 | 179 | assert_eq!(g1, g2); 180 | assert_eq!(g1, g3); 181 | assert_eq!(g1, g4); 182 | assert_eq!(g1, g5); 183 | assert_eq!(g1, g6); 184 | assert_eq!(g1, g7); 185 | assert_eq!(g1, g8); 186 | assert_eq!(g1, g9); 187 | } 188 | } 189 | } 190 | 191 | fn random_negation_tests() { 192 | let mut rng = XorShiftRng::from_seed([ 193 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 194 | 0xe5, 195 | ]); 196 | 197 | for _ in 0..1000 { 198 | let r = G::random(&mut rng); 199 | 200 | let s = G::Scalar::random(&mut rng); 201 | let sneg = s.neg(); 202 | 203 | let mut t1 = r; 204 | t1.mul_assign(s); 205 | 206 | let mut t2 = r; 207 | t2.mul_assign(sneg); 208 | 209 | let mut t3 = t1; 210 | t3.add_assign(&t2); 211 | assert!(bool::from(t3.is_identity())); 212 | 213 | let mut t4 = t1; 214 | t4.add_assign(&t2.to_affine()); 215 | assert!(bool::from(t4.is_identity())); 216 | 217 | assert_eq!(t1.neg(), t2); 218 | } 219 | } 220 | 221 | fn random_doubling_tests() { 222 | let mut rng = XorShiftRng::from_seed([ 223 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 224 | 0xe5, 225 | ]); 226 | 227 | for _ in 0..1000 { 228 | let mut a = G::random(&mut rng); 229 | let mut b = G::random(&mut rng); 230 | 231 | // 2(a + b) 232 | let tmp1 = (a + b).double(); 233 | 234 | // 2a + 2b 235 | a = a.double(); 236 | b = b.double(); 237 | 238 | let mut tmp2 = a; 239 | tmp2.add_assign(&b); 240 | 241 | let mut tmp3 = a; 242 | tmp3.add_assign(&b.to_affine()); 243 | 244 | assert_eq!(tmp1, tmp2); 245 | assert_eq!(tmp1, tmp3); 246 | } 247 | } 248 | 249 | fn random_multiplication_tests() { 250 | let mut rng = XorShiftRng::from_seed([ 251 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 252 | 0xe5, 253 | ]); 254 | 255 | for _ in 0..1000 { 256 | let mut a = G::random(&mut rng); 257 | let mut b = G::random(&mut rng); 258 | let a_affine = a.to_affine(); 259 | let b_affine = b.to_affine(); 260 | 261 | let s = G::Scalar::random(&mut rng); 262 | 263 | // s ( a + b ) 264 | let mut tmp1 = a; 265 | tmp1.add_assign(&b); 266 | tmp1.mul_assign(s); 267 | 268 | // sa + sb 269 | a.mul_assign(s); 270 | b.mul_assign(s); 271 | 272 | let mut tmp2 = a; 273 | tmp2.add_assign(&b); 274 | 275 | // Affine multiplication 276 | let mut tmp3 = Mul::::mul(a_affine, s); 277 | tmp3.add_assign(Mul::::mul(b_affine, s)); 278 | 279 | assert_eq!(tmp1, tmp2); 280 | assert_eq!(tmp1, tmp3); 281 | } 282 | } 283 | 284 | fn random_addition_tests() { 285 | let mut rng = XorShiftRng::from_seed([ 286 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 287 | 0xe5, 288 | ]); 289 | 290 | for _ in 0..1000 { 291 | let a = G::random(&mut rng); 292 | let b = G::random(&mut rng); 293 | let c = G::random(&mut rng); 294 | let a_affine = a.to_affine(); 295 | let b_affine = b.to_affine(); 296 | let c_affine = c.to_affine(); 297 | 298 | // a + a should equal the doubling 299 | { 300 | let mut aplusa = a; 301 | aplusa.add_assign(&a); 302 | 303 | let mut aplusamixed = a; 304 | aplusamixed.add_assign(&a.to_affine()); 305 | 306 | let adouble = a.double(); 307 | 308 | assert_eq!(aplusa, adouble); 309 | assert_eq!(aplusa, aplusamixed); 310 | } 311 | 312 | let mut tmp = vec![G::identity(); 6]; 313 | 314 | // (a + b) + c 315 | tmp[0] = a; 316 | tmp[0].add_assign(&b); 317 | tmp[0].add_assign(&c); 318 | 319 | // a + (b + c) 320 | tmp[1] = b; 321 | tmp[1].add_assign(&c); 322 | tmp[1].add_assign(&a); 323 | 324 | // (a + c) + b 325 | tmp[2] = a; 326 | tmp[2].add_assign(&c); 327 | tmp[2].add_assign(&b); 328 | 329 | // Mixed addition 330 | 331 | // (a + b) + c 332 | tmp[3] = a_affine.to_curve(); 333 | tmp[3].add_assign(&b_affine); 334 | tmp[3].add_assign(&c_affine); 335 | 336 | // a + (b + c) 337 | tmp[4] = b_affine.to_curve(); 338 | tmp[4].add_assign(&c_affine); 339 | tmp[4].add_assign(&a_affine); 340 | 341 | // (a + c) + b 342 | tmp[5] = a_affine.to_curve(); 343 | tmp[5].add_assign(&c_affine); 344 | tmp[5].add_assign(&b_affine); 345 | 346 | // Comparisons 347 | for i in 0..6 { 348 | for j in 0..6 { 349 | assert_eq!(tmp[i], tmp[j]); 350 | assert_eq!(tmp[i].to_affine(), tmp[j].to_affine()); 351 | } 352 | 353 | assert!(tmp[i] != a); 354 | assert!(tmp[i] != b); 355 | assert!(tmp[i] != c); 356 | 357 | assert!(a != tmp[i]); 358 | assert!(b != tmp[i]); 359 | assert!(c != tmp[i]); 360 | } 361 | } 362 | } 363 | 364 | fn random_transformation_tests() { 365 | let mut rng = XorShiftRng::from_seed([ 366 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 367 | 0xe5, 368 | ]); 369 | 370 | for _ in 0..1000 { 371 | let g = G::random(&mut rng); 372 | let g_affine = g.to_affine(); 373 | let g_projective = g_affine.to_curve(); 374 | assert_eq!(g, g_projective); 375 | } 376 | 377 | // Batch normalization 378 | for _ in 0..10 { 379 | let mut v = (0..1000).map(|_| G::random(&mut rng)).collect::>(); 380 | 381 | use rand::distributions::{Distribution, Uniform}; 382 | let between = Uniform::new(0, 1000); 383 | // Sprinkle in some normalized points 384 | for _ in 0..5 { 385 | v[between.sample(&mut rng)] = G::identity(); 386 | } 387 | for _ in 0..5 { 388 | let s = between.sample(&mut rng); 389 | v[s] = v[s].to_affine().to_curve(); 390 | } 391 | 392 | let expected_v = v.iter().map(|v| v.to_affine()).collect::>(); 393 | 394 | let mut normalized = vec![G::Affine::identity(); v.len()]; 395 | G::batch_normalize(&v, &mut normalized); 396 | 397 | assert_eq!(normalized, expected_v); 398 | } 399 | } 400 | 401 | fn random_compressed_encoding_tests() { 402 | let mut rng = XorShiftRng::from_seed([ 403 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 404 | 0xe5, 405 | ]); 406 | 407 | assert_eq!( 408 | G::Affine::from_bytes(&G::Affine::identity().to_bytes()).unwrap(), 409 | G::Affine::identity() 410 | ); 411 | 412 | for _ in 0..1000 { 413 | let mut r = G::random(&mut rng).to_affine(); 414 | 415 | let compressed = r.to_bytes(); 416 | let de_compressed = G::Affine::from_bytes(&compressed).unwrap(); 417 | assert_eq!(de_compressed, r); 418 | 419 | r = r.neg(); 420 | 421 | let compressed = r.to_bytes(); 422 | let de_compressed = G::Affine::from_bytes(&compressed).unwrap(); 423 | assert_eq!(de_compressed, r); 424 | } 425 | } 426 | 427 | pub fn random_uncompressed_encoding_tests() 428 | where 429 | ::Affine: UncompressedEncoding, 430 | { 431 | let mut rng = XorShiftRng::from_seed([ 432 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 433 | 0xe5, 434 | ]); 435 | 436 | assert_eq!( 437 | G::Affine::from_uncompressed(&G::Affine::identity().to_uncompressed()).unwrap(), 438 | G::Affine::identity() 439 | ); 440 | 441 | for _ in 0..1000 { 442 | let r = G::random(&mut rng).to_affine(); 443 | 444 | let uncompressed = r.to_uncompressed(); 445 | let de_uncompressed = G::Affine::from_uncompressed(&uncompressed).unwrap(); 446 | assert_eq!(de_uncompressed, r); 447 | } 448 | } 449 | -------------------------------------------------------------------------------- /src/wnaf.rs: -------------------------------------------------------------------------------- 1 | use alloc::vec::Vec; 2 | use core::iter; 3 | use core::marker::PhantomData; 4 | use core::ops::Mul; 5 | 6 | use ff::PrimeField; 7 | 8 | use super::Group; 9 | 10 | /// Extension trait on a [`Group`] that provides helpers used by [`Wnaf`]. 11 | pub trait WnafGroup: Group { 12 | /// Recommends a wNAF window size given the number of scalars you intend to multiply 13 | /// a base by. Always returns a number between 2 and 22, inclusive. 14 | fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize; 15 | } 16 | 17 | /// Replaces the contents of `table` with a w-NAF window table for the given window size. 18 | pub(crate) fn wnaf_table(table: &mut Vec, mut base: G, window: usize) { 19 | table.truncate(0); 20 | table.reserve(1 << (window - 1)); 21 | 22 | let dbl = base.double(); 23 | 24 | for _ in 0..(1 << (window - 1)) { 25 | table.push(base); 26 | base.add_assign(&dbl); 27 | } 28 | } 29 | 30 | /// This struct represents a view of a sequence of bytes as a sequence of 31 | /// `u64` limbs in little-endian byte order. It maintains a current index, and 32 | /// allows access to the limb at that index and the one following it. Bytes 33 | /// beyond the end of the original buffer are treated as zero. 34 | struct LimbBuffer<'a> { 35 | buf: &'a [u8], 36 | cur_idx: usize, 37 | cur_limb: u64, 38 | next_limb: u64, 39 | } 40 | 41 | impl<'a> LimbBuffer<'a> { 42 | fn new(buf: &'a [u8]) -> Self { 43 | let mut ret = Self { 44 | buf, 45 | cur_idx: 0, 46 | cur_limb: 0, 47 | next_limb: 0, 48 | }; 49 | 50 | // Initialise the limb buffers. 51 | ret.increment_limb(); 52 | ret.increment_limb(); 53 | ret.cur_idx = 0usize; 54 | 55 | ret 56 | } 57 | 58 | fn increment_limb(&mut self) { 59 | self.cur_idx += 1; 60 | self.cur_limb = self.next_limb; 61 | match self.buf.len() { 62 | // There are no more bytes in the buffer; zero-extend. 63 | 0 => self.next_limb = 0, 64 | 65 | // There are fewer bytes in the buffer than a u64 limb; zero-extend. 66 | x @ 1..=7 => { 67 | let mut next_limb = [0; 8]; 68 | next_limb[..x].copy_from_slice(self.buf); 69 | self.next_limb = u64::from_le_bytes(next_limb); 70 | self.buf = &[]; 71 | } 72 | 73 | // There are at least eight bytes in the buffer; read the next u64 limb. 74 | _ => { 75 | let (next_limb, rest) = self.buf.split_at(8); 76 | self.next_limb = u64::from_le_bytes(next_limb.try_into().unwrap()); 77 | self.buf = rest; 78 | } 79 | } 80 | } 81 | 82 | fn get(&mut self, idx: usize) -> (u64, u64) { 83 | assert!([self.cur_idx, self.cur_idx + 1].contains(&idx)); 84 | if idx > self.cur_idx { 85 | self.increment_limb(); 86 | } 87 | (self.cur_limb, self.next_limb) 88 | } 89 | } 90 | 91 | /// Replaces the contents of `wnaf` with the w-NAF representation of a little-endian 92 | /// scalar. 93 | pub(crate) fn wnaf_form>(wnaf: &mut Vec, c: S, window: usize) { 94 | // Required by the NAF definition 95 | debug_assert!(window >= 2); 96 | // Required so that the NAF digits fit in i64 97 | debug_assert!(window <= 64); 98 | 99 | let bit_len = c.as_ref().len() * 8; 100 | 101 | wnaf.truncate(0); 102 | wnaf.reserve(bit_len); 103 | 104 | // Initialise the current and next limb buffers. 105 | let mut limbs = LimbBuffer::new(c.as_ref()); 106 | 107 | let width = 1u64 << window; 108 | let window_mask = width - 1; 109 | 110 | let mut pos = 0; 111 | let mut carry = 0; 112 | while pos < bit_len { 113 | // Construct a buffer of bits of the scalar, starting at bit `pos` 114 | let u64_idx = pos / 64; 115 | let bit_idx = pos % 64; 116 | let (cur_u64, next_u64) = limbs.get(u64_idx); 117 | let bit_buf = if bit_idx + window < 64 { 118 | // This window's bits are contained in a single u64 119 | cur_u64 >> bit_idx 120 | } else { 121 | // Combine the current u64's bits with the bits from the next u64 122 | (cur_u64 >> bit_idx) | (next_u64 << (64 - bit_idx)) 123 | }; 124 | 125 | // Add the carry into the current window 126 | let window_val = carry + (bit_buf & window_mask); 127 | 128 | if window_val & 1 == 0 { 129 | // If the window value is even, preserve the carry and emit 0. 130 | // Why is the carry preserved? 131 | // If carry == 0 and window_val & 1 == 0, then the next carry should be 0 132 | // If carry == 1 and window_val & 1 == 0, then bit_buf & 1 == 1 so the next carry should be 1 133 | wnaf.push(0); 134 | pos += 1; 135 | } else { 136 | wnaf.push(if window_val < width / 2 { 137 | carry = 0; 138 | window_val as i64 139 | } else { 140 | carry = 1; 141 | (window_val as i64).wrapping_sub(width as i64) 142 | }); 143 | wnaf.extend(iter::repeat(0).take(window - 1)); 144 | pos += window; 145 | } 146 | } 147 | } 148 | 149 | /// Performs w-NAF exponentiation with the provided window table and w-NAF form scalar. 150 | /// 151 | /// This function must be provided a `table` and `wnaf` that were constructed with 152 | /// the same window size; otherwise, it may panic or produce invalid results. 153 | pub(crate) fn wnaf_exp(table: &[G], wnaf: &[i64]) -> G { 154 | let mut result = G::identity(); 155 | 156 | let mut found_one = false; 157 | 158 | for n in wnaf.iter().rev() { 159 | if found_one { 160 | result = result.double(); 161 | } 162 | 163 | if *n != 0 { 164 | found_one = true; 165 | 166 | if *n > 0 { 167 | result += &table[(n / 2) as usize]; 168 | } else { 169 | result -= &table[((-n) / 2) as usize]; 170 | } 171 | } 172 | } 173 | 174 | result 175 | } 176 | 177 | /// A "w-ary non-adjacent form" scalar multiplication (also known as exponentiation) 178 | /// context. 179 | /// 180 | /// # Examples 181 | /// 182 | /// This struct can be used to implement several patterns: 183 | /// 184 | /// ## One base, one scalar 185 | /// 186 | /// For this pattern, you can use a transient `Wnaf` context: 187 | /// 188 | /// ```ignore 189 | /// use group::Wnaf; 190 | /// 191 | /// let result = Wnaf::new().scalar(&scalar).base(base); 192 | /// ``` 193 | /// 194 | /// ## Many bases, one scalar 195 | /// 196 | /// For this pattern, you create a `Wnaf` context, load the scalar into it, and then 197 | /// process each base in turn: 198 | /// 199 | /// ```ignore 200 | /// use group::Wnaf; 201 | /// 202 | /// let mut wnaf = Wnaf::new(); 203 | /// let mut wnaf_scalar = wnaf.scalar(&scalar); 204 | /// let results: Vec<_> = bases 205 | /// .into_iter() 206 | /// .map(|base| wnaf_scalar.base(base)) 207 | /// .collect(); 208 | /// ``` 209 | /// 210 | /// ## One base, many scalars 211 | /// 212 | /// For this pattern, you create a `Wnaf` context, load the base into it, and then process 213 | /// each scalar in turn: 214 | /// 215 | /// ```ignore 216 | /// use group::Wnaf; 217 | /// 218 | /// let mut wnaf = Wnaf::new(); 219 | /// let mut wnaf_base = wnaf.base(base, scalars.len()); 220 | /// let results: Vec<_> = scalars 221 | /// .iter() 222 | /// .map(|scalar| wnaf_base.scalar(scalar)) 223 | /// .collect(); 224 | /// ``` 225 | /// 226 | /// ## Many bases, many scalars 227 | /// 228 | /// Say you have `n` bases and `m` scalars, and want to produce `n * m` results. For this 229 | /// pattern, you need to cache the w-NAF tables for the bases and then compute the w-NAF 230 | /// form of the scalars on the fly for every base, or vice versa: 231 | /// 232 | /// ```ignore 233 | /// use group::Wnaf; 234 | /// 235 | /// let mut wnaf_contexts: Vec<_> = (0..bases.len()).map(|_| Wnaf::new()).collect(); 236 | /// let mut wnaf_bases: Vec<_> = wnaf_contexts 237 | /// .iter_mut() 238 | /// .zip(bases) 239 | /// .map(|(wnaf, base)| wnaf.base(base, scalars.len())) 240 | /// .collect(); 241 | /// let results: Vec<_> = wnaf_bases 242 | /// .iter() 243 | /// .flat_map(|wnaf_base| scalars.iter().map(|scalar| wnaf_base.scalar(scalar))) 244 | /// .collect(); 245 | /// ``` 246 | /// 247 | /// Alternatively, use the [`WnafBase`] and [`WnafScalar`] types, which enable the various 248 | /// tables and w-NAF forms to be cached individually per base and scalar. These types can 249 | /// then be directly multiplied without any additional runtime work, at the cost of fixing 250 | /// a specific window size (rather than choosing the window size dynamically). 251 | #[derive(Debug)] 252 | pub struct Wnaf { 253 | base: B, 254 | scalar: S, 255 | window_size: W, 256 | } 257 | 258 | impl Wnaf<(), Vec, Vec> { 259 | /// Construct a new wNAF context without allocating. 260 | pub fn new() -> Self { 261 | Wnaf { 262 | base: vec![], 263 | scalar: vec![], 264 | window_size: (), 265 | } 266 | } 267 | } 268 | 269 | #[cfg(feature = "wnaf-memuse")] 270 | impl memuse::DynamicUsage for Wnaf<(), Vec, Vec> { 271 | fn dynamic_usage(&self) -> usize { 272 | self.base.dynamic_usage() + self.scalar.dynamic_usage() 273 | } 274 | 275 | fn dynamic_usage_bounds(&self) -> (usize, Option) { 276 | let (base_lower, base_upper) = self.base.dynamic_usage_bounds(); 277 | let (scalar_lower, scalar_upper) = self.scalar.dynamic_usage_bounds(); 278 | 279 | ( 280 | base_lower + scalar_lower, 281 | base_upper.zip(scalar_upper).map(|(a, b)| a + b), 282 | ) 283 | } 284 | } 285 | 286 | impl Wnaf<(), Vec, Vec> { 287 | /// Given a base and a number of scalars, compute a window table and return a `Wnaf` object that 288 | /// can perform exponentiations with `.scalar(..)`. 289 | pub fn base(&mut self, base: G, num_scalars: usize) -> Wnaf> { 290 | // Compute the appropriate window size based on the number of scalars. 291 | let window_size = G::recommended_wnaf_for_num_scalars(num_scalars); 292 | 293 | // Compute a wNAF table for the provided base and window size. 294 | wnaf_table(&mut self.base, base, window_size); 295 | 296 | // Return a Wnaf object that immutably borrows the computed base storage location, 297 | // but mutably borrows the scalar storage location. 298 | Wnaf { 299 | base: &self.base[..], 300 | scalar: &mut self.scalar, 301 | window_size, 302 | } 303 | } 304 | 305 | /// Given a scalar, compute its wNAF representation and return a `Wnaf` object that can perform 306 | /// exponentiations with `.base(..)`. 307 | pub fn scalar(&mut self, scalar: &::Scalar) -> Wnaf, &[i64]> { 308 | // We hard-code a window size of 4. 309 | let window_size = 4; 310 | 311 | // Compute the wNAF form of the scalar. 312 | wnaf_form(&mut self.scalar, scalar.to_repr(), window_size); 313 | 314 | // Return a Wnaf object that mutably borrows the base storage location, but 315 | // immutably borrows the computed wNAF form scalar location. 316 | Wnaf { 317 | base: &mut self.base, 318 | scalar: &self.scalar[..], 319 | window_size, 320 | } 321 | } 322 | } 323 | 324 | impl<'a, G: Group> Wnaf> { 325 | /// Constructs new space for the scalar representation while borrowing 326 | /// the computed window table, for sending the window table across threads. 327 | pub fn shared(&self) -> Wnaf> { 328 | Wnaf { 329 | base: self.base, 330 | scalar: vec![], 331 | window_size: self.window_size, 332 | } 333 | } 334 | } 335 | 336 | #[cfg(feature = "wnaf-memuse")] 337 | impl<'a, G: Group> memuse::DynamicUsage for Wnaf> { 338 | fn dynamic_usage(&self) -> usize { 339 | // The heap memory for the window table is counted in the parent `Wnaf`. 340 | self.scalar.dynamic_usage() 341 | } 342 | 343 | fn dynamic_usage_bounds(&self) -> (usize, Option) { 344 | self.scalar.dynamic_usage_bounds() 345 | } 346 | } 347 | 348 | impl<'a, G: Group> Wnaf, &'a [i64]> { 349 | /// Constructs new space for the window table while borrowing 350 | /// the computed scalar representation, for sending the scalar representation 351 | /// across threads. 352 | pub fn shared(&self) -> Wnaf, &'a [i64]> { 353 | Wnaf { 354 | base: vec![], 355 | scalar: self.scalar, 356 | window_size: self.window_size, 357 | } 358 | } 359 | } 360 | 361 | #[cfg(feature = "wnaf-memuse")] 362 | impl<'a, G: Group + memuse::DynamicUsage> memuse::DynamicUsage for Wnaf, &'a [i64]> { 363 | fn dynamic_usage(&self) -> usize { 364 | // The heap memory for the scalar representation is counted in the parent `Wnaf`. 365 | self.base.dynamic_usage() 366 | } 367 | 368 | fn dynamic_usage_bounds(&self) -> (usize, Option) { 369 | self.base.dynamic_usage_bounds() 370 | } 371 | } 372 | 373 | impl> Wnaf { 374 | /// Performs exponentiation given a base. 375 | pub fn base(&mut self, base: G) -> G 376 | where 377 | B: AsMut>, 378 | { 379 | wnaf_table(self.base.as_mut(), base, self.window_size); 380 | wnaf_exp(self.base.as_mut(), self.scalar.as_ref()) 381 | } 382 | } 383 | 384 | impl>> Wnaf { 385 | /// Performs exponentiation given a scalar. 386 | pub fn scalar(&mut self, scalar: &::Scalar) -> G 387 | where 388 | B: AsRef<[G]>, 389 | { 390 | wnaf_form(self.scalar.as_mut(), scalar.to_repr(), self.window_size); 391 | wnaf_exp(self.base.as_ref(), self.scalar.as_mut()) 392 | } 393 | } 394 | 395 | /// A "w-ary non-adjacent form" scalar, that uses precomputation to improve the speed of 396 | /// scalar multiplication. 397 | /// 398 | /// # Examples 399 | /// 400 | /// See [`WnafBase`] for usage examples. 401 | #[derive(Clone, Debug)] 402 | pub struct WnafScalar { 403 | wnaf: Vec, 404 | field: PhantomData, 405 | } 406 | 407 | #[cfg(feature = "wnaf-memuse")] 408 | impl memuse::DynamicUsage for WnafScalar { 409 | fn dynamic_usage(&self) -> usize { 410 | self.wnaf.dynamic_usage() 411 | } 412 | 413 | fn dynamic_usage_bounds(&self) -> (usize, Option) { 414 | self.wnaf.dynamic_usage_bounds() 415 | } 416 | } 417 | 418 | impl WnafScalar { 419 | /// Computes the w-NAF representation of the given scalar with the specified 420 | /// `WINDOW_SIZE`. 421 | pub fn new(scalar: &F) -> Self { 422 | let mut wnaf = vec![]; 423 | 424 | // Compute the w-NAF form of the scalar. 425 | wnaf_form(&mut wnaf, scalar.to_repr(), WINDOW_SIZE); 426 | 427 | WnafScalar { 428 | wnaf, 429 | field: PhantomData::default(), 430 | } 431 | } 432 | } 433 | 434 | /// A fixed window table for a group element, precomputed to improve the speed of scalar 435 | /// multiplication. 436 | /// 437 | /// This struct is designed for usage patterns that have long-term cached bases and/or 438 | /// scalars, or [Cartesian products] of bases and scalars. The [`Wnaf`] API enables one or 439 | /// the other to be cached, but requires either the base window tables or the scalar w-NAF 440 | /// forms to be computed repeatedly on the fly, which can become a significant performance 441 | /// issue for some use cases. 442 | /// 443 | /// `WnafBase` and [`WnafScalar`] enable an alternative trade-off: by fixing the window 444 | /// size at compile time, the precomputations are guaranteed to only occur once per base 445 | /// and once per scalar. Users should select their window size based on how long the bases 446 | /// are expected to live; a larger window size will consume more memory and take longer to 447 | /// precompute, but result in faster scalar multiplications. 448 | /// 449 | /// [Cartesian products]: https://en.wikipedia.org/wiki/Cartesian_product 450 | /// 451 | /// # Examples 452 | /// 453 | /// ```ignore 454 | /// use group::{WnafBase, WnafScalar}; 455 | /// 456 | /// let wnaf_bases: Vec<_> = bases.into_iter().map(WnafBase::<_, 4>::new).collect(); 457 | /// let wnaf_scalars: Vec<_> = scalars.iter().map(WnafScalar::new).collect(); 458 | /// let results: Vec<_> = wnaf_bases 459 | /// .iter() 460 | /// .flat_map(|base| wnaf_scalars.iter().map(|scalar| base * scalar)) 461 | /// .collect(); 462 | /// ``` 463 | /// 464 | /// Note that this pattern requires specifying a fixed window size (unlike previous 465 | /// patterns that picked a suitable window size internally). This is necessary to ensure 466 | /// in the type system that the base and scalar `Wnaf`s were computed with the same window 467 | /// size, allowing the result to be computed infallibly. 468 | #[derive(Clone, Debug)] 469 | pub struct WnafBase { 470 | table: Vec, 471 | } 472 | 473 | #[cfg(feature = "wnaf-memuse")] 474 | impl memuse::DynamicUsage 475 | for WnafBase 476 | { 477 | fn dynamic_usage(&self) -> usize { 478 | self.table.dynamic_usage() 479 | } 480 | 481 | fn dynamic_usage_bounds(&self) -> (usize, Option) { 482 | self.table.dynamic_usage_bounds() 483 | } 484 | } 485 | 486 | impl WnafBase { 487 | /// Computes a window table for the given base with the specified `WINDOW_SIZE`. 488 | pub fn new(base: G) -> Self { 489 | let mut table = vec![]; 490 | 491 | // Compute a window table for the provided base and window size. 492 | wnaf_table(&mut table, base, WINDOW_SIZE); 493 | 494 | WnafBase { table } 495 | } 496 | } 497 | 498 | impl Mul<&WnafScalar> 499 | for &WnafBase 500 | { 501 | type Output = G; 502 | 503 | fn mul(self, rhs: &WnafScalar) -> Self::Output { 504 | wnaf_exp(&self.table, &rhs.wnaf) 505 | } 506 | } 507 | --------------------------------------------------------------------------------