├── .config └── nextest.toml ├── .github ├── release-pr-template.ejs └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── README.md ├── benches ├── bn256_field.rs └── less_than.rs ├── build.rs ├── release.toml ├── rust-toolchain ├── script ├── bn256.py └── get_current_version.sh └── src ├── arithmetic.rs ├── bls12_381 └── mod.rs ├── bn256 ├── assembly.rs ├── curve.rs ├── engine.rs ├── fq.rs ├── fq12.rs ├── fq2.rs ├── fq6.rs ├── fr.rs ├── fr │ └── table_tests.rs └── mod.rs ├── derive ├── curve.rs ├── field.rs └── mod.rs ├── ff_inverse.rs ├── ff_jacobi.rs ├── grumpkin ├── curve.rs └── mod.rs ├── hash_to_curve.rs ├── lib.rs ├── pairing.rs ├── pasta └── mod.rs ├── secp256k1 ├── curve.rs ├── fp.rs ├── fq.rs └── mod.rs ├── secp256r1 ├── curve.rs ├── fp.rs ├── fq.rs └── mod.rs ├── serde.rs └── tests ├── curve.rs ├── field.rs └── mod.rs /.config/nextest.toml: -------------------------------------------------------------------------------- 1 | [profile.ci] 2 | # Print out output for failing tests as soon as they fail, and also at the end 3 | # of the run (for easy scrollability). 4 | failure-output = "immediate-final" 5 | # Show skipped tests in the CI output. 6 | status-level = "skip" 7 | # Do not cancel the test run on the first failure. 8 | fail-fast = false 9 | # Mark tests as slow after 5mins, kill them after 50 10 | slow-timeout = { period = "300s", terminate-after = 10 } 11 | -------------------------------------------------------------------------------- /.github/release-pr-template.ejs: -------------------------------------------------------------------------------- 1 | This is a release PR for version **<%= version.actual %>**<% 2 | if (version.actual != version.desired) { 3 | %> (performing a <%= version.desired %> bump).<% 4 | } else { 5 | %>.<% 6 | } 7 | %> 8 | 9 | You will still need to manually publish the cargo crate: 10 | 11 | ``` 12 | $ make VERSION=<%= version.actual %> release 13 | ``` 14 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI Check 2 | on: 3 | merge_group: 4 | pull_request: 5 | push: 6 | branches: 7 | - main 8 | env: 9 | CARGO_TERM_COLOR: always 10 | # Disable incremental compilation. 11 | # 12 | # Incremental compilation is useful as part of an edit-build-test-edit cycle, 13 | # as it lets the compiler avoid recompiling code that hasn't changed. However, 14 | # on CI, we're not making small edits; we're almost always building the entire 15 | # project from scratch. Thus, incremental compilation on CI actually 16 | # introduces *additional* overhead to support making future builds 17 | # faster...but no future builds will ever occur in any given CI environment. 18 | # 19 | # See https://matklad.github.io/2021/09/04/fast-rust-builds.html#ci-workflow 20 | # for details. 21 | CARGO_INCREMENTAL: 0 22 | # Allow more retries for network requests in cargo (downloading crates) and 23 | # rustup (installing toolchains). This should help to reduce flaky CI failures 24 | # from transient network timeouts or other issues. 25 | CARGO_NET_RETRY: 10 26 | RUSTUP_MAX_RETRIES: 10 27 | # Don't emit giant backtraces in the CI logs. 28 | RUST_BACKTRACE: short 29 | 30 | # Jobs launched for a PR event cancel the ongoing one for the same workflow + PR, 31 | # Only retries (of the same run) for a Push event cancel the prior one. 32 | concurrency: 33 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 34 | cancel-in-progress: true 35 | 36 | 37 | jobs: 38 | test: 39 | if: github.event.pull_request.draft == false 40 | name: Test 41 | runs-on: ubuntu-latest 42 | strategy: 43 | matrix: 44 | include: 45 | - feature: 46 | - feature: default 47 | steps: 48 | - uses: actions/checkout@v2 49 | - uses: actions-rs/toolchain@v1 50 | # use the more efficient nextest 51 | - uses: taiki-e/install-action@nextest 52 | - uses: Swatinem/rust-cache@v2 53 | - name: Build 54 | # This build will be reused by nextest, 55 | # and also checks (--all-targets) that benches don't bit-rot 56 | run: cargo build --release --all-targets --no-default-features --features "${{ matrix.feature }}" 57 | - name: Test 58 | run: | 59 | cargo nextest run --profile ci --release --workspace --no-default-features --features "${{ matrix.feature }}" 60 | - name: Doctests # nextest does not support doc tests 61 | run: | 62 | cargo test --doc 63 | 64 | fmt: 65 | if: github.event.pull_request.draft == false 66 | name: Rustfmt 67 | timeout-minutes: 30 68 | runs-on: ubuntu-latest 69 | steps: 70 | - uses: actions/checkout@v2 71 | - uses: actions-rs/toolchain@v1 72 | - uses: Swatinem/rust-cache@v2 73 | - run: rustup component add rustfmt 74 | - uses: actions-rs/cargo@v1 75 | with: 76 | command: fmt 77 | args: --all -- --check 78 | 79 | clippy: 80 | if: github.event.pull_request.draft == false 81 | name: Clippy lint checks 82 | runs-on: ubuntu-latest 83 | steps: 84 | - uses: actions/checkout@v2 85 | - uses: actions-rs/toolchain@v1 86 | with: 87 | components: clippy 88 | - uses: Swatinem/rust-cache@v2 89 | - name: Run clippy 90 | uses: actions-rs/cargo@v1 91 | with: 92 | command: clippy 93 | args: --verbose --release --tests --all-features 94 | 95 | bench: 96 | if: github.event.pull_request.draft == false 97 | name: Bench 98 | runs-on: ubuntu-latest 99 | strategy: 100 | matrix: 101 | include: 102 | - feature: default 103 | - feature: asm 104 | - feature: bn256-table 105 | steps: 106 | - uses: actions/checkout@v2 107 | - uses: actions-rs/toolchain@v1 108 | - uses: Swatinem/rust-cache@v2 109 | - name: Bench arithmetic 110 | uses: actions-rs/cargo@v1 111 | with: 112 | command: test 113 | args: --profile bench test_field -- --nocapture 114 | - name: Bench assembly arithmetic 115 | uses: actions-rs/cargo@v1 116 | with: 117 | command: test 118 | args: --profile bench test_field --features ${{ matrix.feature }} -- --nocapture 119 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Open a release PR 2 | on: 3 | workflow_dispatch: 4 | inputs: 5 | version: 6 | description: Version to release 7 | required: true 8 | type: string 9 | env: 10 | RUSTUP_MAX_RETRIES: 10 11 | 12 | jobs: 13 | make-release-pr: 14 | runs-on: ubuntu-latest 15 | strategy: 16 | matrix: 17 | package: [halo2curves] 18 | steps: 19 | - name: Checkout code 20 | uses: actions/checkout@v3 21 | - uses: actions-rs/toolchain@v1 22 | with: 23 | profile: minimal 24 | 25 | - name: Get current version 26 | id: get-version 27 | run: | 28 | echo "CURRENT_VERSION=$(./script/get_current_version.sh ${{ matrix.package }})" >> $GITHUB_ENV 29 | 30 | - name: Check version 31 | id: version-check 32 | run: | 33 | IFS='.' read -ra INPUT_VERSION <<< "${{ github.event.inputs.version }}" 34 | IFS='.' read -ra CURRENT_VERSION <<< "${{ env.CURRENT_VERSION }}" 35 | 36 | for i in "${!INPUT_VERSION[@]}"; do 37 | if (( INPUT_VERSION[i] > CURRENT_VERSION[i] )); then 38 | echo "Input version is larger than current version. Proceeding..." 39 | exit 0 40 | elif (( INPUT_VERSION[i] < CURRENT_VERSION[i] )); then 41 | echo "Input version is not larger than current version. Failing..." 42 | exit 1 43 | fi 44 | done 45 | 46 | echo "Input version is equal to current version. Failing..." 47 | exit 1 48 | 49 | - name: Install cargo-release 50 | uses: taiki-e/install-action@v2 51 | with: 52 | tool: cargo-release 53 | 54 | - uses: cargo-bins/release-pr@v2 55 | with: 56 | pr-template-file: .github/release-pr-template.ejs 57 | github-token: ${{ secrets.GITHUB_TOKEN }} 58 | version: ${{ inputs.version }} 59 | crate-release-all: true 60 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | **/*.rs.bk 4 | .vscode 5 | **/*.html 6 | .DS_Store 7 | 8 | # script generated source code 9 | src/bn256/fr/table.rs 10 | 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "halo2curves" 3 | version = "0.1.0" 4 | authors = ["Privacy Scaling Explorations team"] 5 | license = "MIT/Apache-2.0" 6 | edition = "2021" 7 | repository = "https://github.com/privacy-scaling-explorations/halo2curves" 8 | readme = "README.md" 9 | description = "Elliptic curve implementations and wrappers for halo2 library" 10 | rust-version = "1.63.0" 11 | 12 | [dev-dependencies] 13 | criterion = { version = "0.3", features = ["html_reports"] } 14 | rand_xorshift = "0.3" 15 | ark-std = { version = "0.3" } 16 | bincode = "1.3.3" 17 | 18 | [dependencies] 19 | subtle = "2.4" 20 | ff = { version = "0.13.0", default-features = false, features = ["std"] } 21 | group = "0.13.0" 22 | pasta_curves = "0.5.0" 23 | static_assertions = "1.1.0" 24 | rand = "0.8" 25 | rand_core = { version = "0.6", default-features = false } 26 | lazy_static = "1.4.0" 27 | num-bigint = "0.4.3" 28 | num-traits = "0.2" 29 | axiom-pairing = { package = "pairing", version = "0.23" } 30 | paste = "1.0.11" 31 | serde = { version = "1.0", default-features = false, optional = true } 32 | serde_arrays = { version = "0.1.0", optional = true } 33 | blake2b_simd = "1" 34 | bls12_381 = { git = "https://github.com/privacy-scaling-explorations/bls12_381", tag = "v2023_10_26", features = ["groups", "basefield"] } 35 | maybe-rayon = { version = "0.1.0", default-features = false } 36 | 37 | [features] 38 | default = ["reexport", "bits"] 39 | asm = [] 40 | bits = ["ff/bits"] 41 | bn256-table = [] 42 | derive_serde = ["serde/derive", "serde_arrays"] 43 | prefetch = [] 44 | print-trace = ["ark-std/print-trace"] 45 | reexport = [] 46 | 47 | [profile.bench] 48 | opt-level = 3 49 | debug = false 50 | debug-assertions = false 51 | overflow-checks = false 52 | lto = true 53 | incremental = false 54 | codegen-units = 1 55 | 56 | [[bench]] 57 | name = "less_than" 58 | harness = false 59 | 60 | [[bench]] 61 | name = "bn256_field" 62 | harness = false 63 | required-features = ["reexport"] 64 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | doc: 2 | cargo doc --no-deps 3 | 4 | .PHONY: doc 5 | 6 | # Since the master branch is protected, the current workflow is to create a PR with the version changes, 7 | # and once the PR is merged, run the `make VERSION= release` to publish the new crates. 8 | release: 9 | ifndef VERSION 10 | $(error VERSION is not set. Run with `make VERSION= release`) 11 | endif 12 | ifeq (, $(shell cargo --list|grep release)) 13 | $(error "Please, install cargo-release in order to be able to use this rule") 14 | endif 15 | git pull 16 | cargo update 17 | git tag v$(VERSION) 18 | @TRACKING_BRANCH=$$(git rev-parse --abbrev-ref --symbolic-full-name @{u} 2> /dev/null) ;\ 19 | if [ "$$TRACKING_BRANCH" == "" ]; then \ 20 | echo "Error: Current branch does not have an upstream tracking branch." ;\ 21 | exit 1 ;\ 22 | fi ;\ 23 | git push --tags $$(echo $$TRACKING_BRANCH | sed 's!/.*!!') v$(VERSION) 24 | cargo release publish --execute --verbose -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # A collection of Elliptic Curves for ZkCrypto traits 2 | 3 | This library provides efficient and flexible implementations of various halo2-friendly elliptic curves, originally implementing the BN256 curve with traits from the `zkcrypto` ecosystem, 4 | 5 | * [`zkcrypto/ff`](https://github.com/zkcrypto/ff) 6 | * [`zkcrypto/group`](https://github.com/zkcrypto/group) 7 | * [`zkcrypto/pairing`](https://github.com/zkcrypto/pairing) 8 | 9 | The implementations were originally ported from [matterlabs/pairing](https://github.com/matter-labs/pairing/tree/master/src/bn256) and [zkcrypto/bls12-381](https://github.com/zkcrypto/bls12_381), but have been extended and optimized to cover a broader set of curves and use cases. Since its initial release, the library has expanded to include additional curves, along with the following features: 10 | 11 | * `secp256k1`, `secp256r1`, and `grumpkin` curves, enhancing its usability across a range of cryptographic protocols. 12 | * Assembly optimizations leading to significantly improved performance. 13 | * Various features related to serialization and deserialization of curve points and field elements. 14 | * Curve-specific optimizations and benchmarking capabilities. 15 | 16 | ## Benchmarks 17 | 18 | Benchmarking is supported through the use of Rust's built-in test framework. Benchmarks can be run without assembly optimizations: 19 | 20 | ``` 21 | $ cargo test --profile bench test_field -- --nocapture 22 | ``` 23 | 24 | or with assembly optimizations: 25 | 26 | ``` 27 | $ cargo test --profile bench test_field --features asm -- --nocapture 28 | ``` 29 | 30 | 31 | ## Additional Features 32 | 33 | 1. **Derivation of Serialize/Deserialize**: The library supports Serde's `Serialize` and `Deserialize` traits for field and group elements, making it easier to integrate curve operations into serialization-dependent workflows. 34 | 35 | 2. **Hash to Curve**: For the `bn256::G1` and `grumpkin::G1` curves, `hash_to_curve` is implemented, enabling more efficient hash-and-sign signature schemes. 36 | 37 | 3. **Lookup Table**: A pre-computed lookup table is available for `bn256::Fr`, accelerating conversion from `u16` to montgomery representation. 38 | 39 | ## Structure 40 | 41 | The library's top-level directories are organized as follows: 42 | 43 | * `benches`: Contains benchmarking tests. 44 | * `script`: Contains utility scripts. 45 | * `src`: Contains the source code of the library, further subdivided into modules for each supported curve (`bn256`, `grumpkin`, `secp256k1`, `secp256r1`, `pasta`) and additional functionalities (`derive`, `tests`). 46 | -------------------------------------------------------------------------------- /benches/bn256_field.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; 2 | use halo2curves::{bn256::*, ff::Field, legendre::Legendre}; 3 | use rand::SeedableRng; 4 | use rand_xorshift::XorShiftRng; 5 | 6 | pub fn bench_bn256_field(c: &mut Criterion) { 7 | let mut rng = XorShiftRng::from_seed([ 8 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 9 | 0xe5, 10 | ]); 11 | 12 | let a = Fq::random(&mut rng); 13 | let b = Fq::random(&mut rng); 14 | 15 | #[cfg(not(feature = "asm"))] 16 | let mut group = c.benchmark_group("BN256 Field Arithmetic (no assembly)"); 17 | 18 | #[cfg(feature = "asm")] 19 | let mut group = c.benchmark_group("BN256 Field Arithmetic (with assembly)"); 20 | 21 | group.significance_level(0.1).sample_size(10000); 22 | group.throughput(Throughput::Elements(1)); 23 | 24 | group.bench_function("bn256_fq_add", |bencher| { 25 | bencher.iter(|| black_box(&a).add(black_box(&b))) 26 | }); 27 | group.bench_function("bn256_fq_double", |bencher| { 28 | bencher.iter(|| black_box(&a).double()) 29 | }); 30 | group.bench_function("bn256_fq_sub", |bencher| { 31 | bencher.iter(|| black_box(&a).sub(black_box(&b))) 32 | }); 33 | group.bench_function("bn256_fq_neg", |bencher| { 34 | bencher.iter(|| black_box(&a).neg()) 35 | }); 36 | group.bench_function("bn256_fq_mul", |bencher| { 37 | bencher.iter(|| black_box(&a).mul(black_box(&b))) 38 | }); 39 | group.bench_function("bn256_fq_square", |bencher| { 40 | bencher.iter(|| black_box(&a).square()) 41 | }); 42 | group.bench_function("bn256_fq_invert", |bencher| { 43 | bencher.iter(|| black_box(&a).invert()) 44 | }); 45 | group.bench_function("bn256_fq_legendre", |bencher| { 46 | bencher.iter(|| black_box(&a).legendre()) 47 | }); 48 | group.bench_function("bn256_fq_jacobi", |bencher| { 49 | bencher.iter(|| black_box(&a).jacobi()) 50 | }); 51 | } 52 | 53 | criterion_group!(benches, bench_bn256_field); 54 | criterion_main!(benches); 55 | -------------------------------------------------------------------------------- /benches/less_than.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused)] 2 | 3 | use criterion::BenchmarkId; 4 | 5 | /// Compute a - (b + borrow), returning the result and the new borrow. 6 | #[inline(always)] 7 | const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { 8 | let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); 9 | (ret as u64, (ret >> 64) as u64) 10 | } 11 | 12 | #[inline(always)] 13 | fn is_less_than(x: &[u64; 4], y: &[u64; 4]) -> bool { 14 | match x[3].cmp(&y[3]) { 15 | core::cmp::Ordering::Less => return true, 16 | core::cmp::Ordering::Greater => return false, 17 | _ => {} 18 | } 19 | match x[2].cmp(&y[2]) { 20 | core::cmp::Ordering::Less => return true, 21 | core::cmp::Ordering::Greater => return false, 22 | _ => {} 23 | } 24 | match x[1].cmp(&y[1]) { 25 | core::cmp::Ordering::Less => return true, 26 | core::cmp::Ordering::Greater => return false, 27 | _ => {} 28 | } 29 | x[0].lt(&y[0]) 30 | } 31 | 32 | #[inline(always)] 33 | fn check_underflow(x: &[u64; 4], y: &[u64; 4]) -> bool { 34 | let (_, borrow) = sbb(x[0], y[0], 0); 35 | let (_, borrow) = sbb(x[1], y[1], borrow); 36 | let (_, borrow) = sbb(x[2], y[2], borrow); 37 | let (_, borrow) = sbb(x[3], y[3], borrow); 38 | borrow >> 63 == 1 39 | } 40 | 41 | use criterion::{criterion_group, criterion_main, Criterion}; 42 | use group::Group; 43 | use halo2curves::bn256::G1; 44 | use rand::SeedableRng; 45 | use rand_xorshift::XorShiftRng; 46 | 47 | pub fn criterion_benchmark(c: &mut Criterion) { 48 | let x: [u64; 4] = [(); 4].map(|_| rand::random()); 49 | let y: [u64; 4] = [(); 4].map(|_| rand::random()); 50 | 51 | let mut group = c.benchmark_group("Big less than methods"); 52 | 53 | group.bench_with_input( 54 | BenchmarkId::new("is_less_than", ""), 55 | &(x, y), 56 | |b, (x, y)| b.iter(|| is_less_than(x, y)), 57 | ); 58 | 59 | group.bench_with_input( 60 | BenchmarkId::new("check_underflow", ""), 61 | &(x, y), 62 | |b, (x, y)| b.iter(|| check_underflow(x, y)), 63 | ); 64 | group.finish(); 65 | } 66 | 67 | pub fn arithmetics(c: &mut Criterion) { 68 | let mut rng = XorShiftRng::from_seed([ 69 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 70 | 0xe5, 71 | ]); 72 | let iteration = 1000; 73 | 74 | let x_vec: Vec = (0..iteration).map(|_| G1::random(&mut rng)).collect(); 75 | let y_vec: Vec = (0..iteration).map(|_| G1::random(&mut rng)).collect(); 76 | 77 | let mut group = c.benchmark_group("Group operations"); 78 | 79 | group.bench_with_input(BenchmarkId::new("double", ""), &x_vec, |b, x_vec| { 80 | b.iter(|| x_vec.iter().map(|x| x.double()).collect::>()) 81 | }); 82 | 83 | group.bench_with_input( 84 | BenchmarkId::new("add", ""), 85 | &(x_vec, y_vec), 86 | |b, (x_vec, y_vec)| { 87 | b.iter(|| { 88 | x_vec 89 | .iter() 90 | .zip(y_vec.iter()) 91 | .map(|(x, y)| x + y) 92 | .collect::>() 93 | }) 94 | }, 95 | ); 96 | 97 | group.finish(); 98 | } 99 | 100 | criterion_group!(benches, criterion_benchmark, arithmetics); 101 | criterion_main!(benches); 102 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | #[cfg(feature = "asm")] 3 | if std::env::consts::ARCH != "x86_64" { 4 | eprintln!("Currently feature `asm` can only be enabled on x86_64 arch."); 5 | std::process::exit(1); 6 | } 7 | #[cfg(feature = "bn256-table")] 8 | { 9 | if std::path::Path::new("src/bn256/fr/table.rs").exists() { 10 | eprintln!("Pre-computed table for BN256 scalar field exists."); 11 | eprintln!("Skip pre-computation\n"); 12 | } else { 13 | eprintln!("Generating pre-computed table for BN256 scalar field\n"); 14 | std::process::Command::new("python3") 15 | .args(["script/bn256.py"]) 16 | .output() 17 | .expect("requires python 3 to build pre-computed table"); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /release.toml: -------------------------------------------------------------------------------- 1 | consolidate-commits = false 2 | -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | 1.70.0 2 | -------------------------------------------------------------------------------- /script/bn256.py: -------------------------------------------------------------------------------- 1 | # This file generates the montogomary form integers for x in [0, 2^16) \intersect 2 | # BN::ScalarField 3 | 4 | verbose = False 5 | 6 | modulus = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 7 | R = 2**256 % modulus 8 | table_size = 1<<16 9 | 10 | # @input: field element a 11 | # @output: 4 u64 a0, a1, a2, a3 s.t. 12 | # a = a3 * 2^192 + a2 * 2^128 + a1 * 2^64 + a0 13 | def decompose_field_element(a): 14 | a0 = a % 2**64 15 | a = a // 2**64 16 | a1 = a % 2**64 17 | a = a // 2**64 18 | a2 = a % 2**64 19 | a = a // 2**64 20 | a3 = a 21 | return [a0, a1, a2, a3] 22 | 23 | 24 | # @input: field element a 25 | # @output: a rust format string that encodes 26 | # 4 u64 a0, a1, a2, a3 s.t. 27 | # a = a3 * 2^192 + a2 * 2^128 + a1 * 2^64 + a0 28 | def format_field_element(a): 29 | [a0, a1, a2, a3] = decompose_field_element(a); 30 | return "Fr([" + hex(a0) + "," + hex(a1) + "," + hex(a2) + "," + hex(a3) + "]),\n" 31 | 32 | 33 | f = open("src/bn256/fr/table.rs", "w") 34 | f.write("//! auto generated file from scripts/bn256.sage, do not modify\n") 35 | f.write("//! see src/bn256/fr.rs for more details\n") 36 | f.write("use super::Fr;\n") 37 | f.write("pub const FR_TABLE: &[Fr] = &[\n") 38 | 39 | for i in range(table_size): 40 | a = (i * R) % modulus 41 | if verbose: 42 | print (i, a, format_field_element(a)) 43 | f.write(format_field_element(a)) 44 | 45 | f.write("\n];") 46 | 47 | -------------------------------------------------------------------------------- /script/get_current_version.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Script requirements: 4 | # - curl 5 | # - jq 6 | 7 | # Fail on first error, on undefined variables, and on failures in pipelines. 8 | set -euo pipefail 9 | 10 | # Verify if required executables are available 11 | for cmd in curl jq git cargo; do 12 | if ! command -v $cmd &> /dev/null 13 | then 14 | echo "Error: $cmd could not be found." 15 | exit 1 16 | fi 17 | done 18 | 19 | # Go to the repo root directory. 20 | cd "$(git rev-parse --show-toplevel)" 21 | 22 | # Check 1 argument is given 23 | if [ $# -lt 1 ] 24 | then 25 | echo "Usage : $0 " 26 | exit 1 27 | fi 28 | 29 | # The first argument should be the name of a crate. 30 | CRATE_NAME="$1" 31 | 32 | cargo metadata --format-version 1 | \ 33 | jq --arg crate_name "$CRATE_NAME" --exit-status -r \ 34 | '.packages[] | select(.name == $crate_name) | .version' 35 | -------------------------------------------------------------------------------- /src/arithmetic.rs: -------------------------------------------------------------------------------- 1 | //! This module provides common utilities, traits and structures for group and 2 | //! field arithmetic. 3 | //! 4 | //! This module is temporary, and the extension traits defined here are expected to be 5 | //! upstreamed into the `ff` and `group` crates after some refactoring. 6 | 7 | use crate::CurveExt; 8 | 9 | pub(crate) struct EndoParameters { 10 | pub(crate) gamma1: [u64; 4], 11 | pub(crate) gamma2: [u64; 4], 12 | pub(crate) b1: [u64; 4], 13 | pub(crate) b2: [u64; 4], 14 | } 15 | 16 | pub trait CurveEndo: CurveExt { 17 | fn decompose_scalar(e: &Self::ScalarExt) -> (u128, bool, u128, bool); 18 | } 19 | 20 | pub trait CurveAffineExt: pasta_curves::arithmetic::CurveAffine { 21 | fn batch_add( 22 | points: &mut [Self], 23 | output_indices: &[u32], 24 | num_points: usize, 25 | offset: usize, 26 | bases: &[Self], 27 | base_positions: &[u32], 28 | ); 29 | 30 | /// Unlike the `Coordinates` trait, this just returns the raw affine coordinates without checking `is_on_curve` 31 | fn into_coordinates(self) -> (Self::Base, Self::Base) { 32 | // fallback implementation 33 | let coordinates = self.coordinates().unwrap(); 34 | (*coordinates.x(), *coordinates.y()) 35 | } 36 | } 37 | 38 | /// Compute a + b + carry, returning the result and the new carry over. 39 | #[inline(always)] 40 | pub(crate) const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { 41 | let ret = (a as u128) + (b as u128) + (carry as u128); 42 | (ret as u64, (ret >> 64) as u64) 43 | } 44 | 45 | /// Compute a - (b + borrow), returning the result and the new borrow. 46 | #[inline(always)] 47 | pub(crate) const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { 48 | let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); 49 | (ret as u64, (ret >> 64) as u64) 50 | } 51 | 52 | /// Compute a + (b * c) + carry, returning the result and the new carry over. 53 | #[inline(always)] 54 | pub(crate) const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { 55 | let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); 56 | (ret as u64, (ret >> 64) as u64) 57 | } 58 | 59 | /// Compute a + (b * c), returning the result and the new carry over. 60 | #[inline(always)] 61 | pub(crate) const fn macx(a: u64, b: u64, c: u64) -> (u64, u64) { 62 | let res = (a as u128) + ((b as u128) * (c as u128)); 63 | (res as u64, (res >> 64) as u64) 64 | } 65 | 66 | /// Compute a * b, returning the result. 67 | #[inline(always)] 68 | pub(crate) fn mul_512(a: [u64; 4], b: [u64; 4]) -> [u64; 8] { 69 | let (r0, carry) = macx(0, a[0], b[0]); 70 | let (r1, carry) = macx(carry, a[0], b[1]); 71 | let (r2, carry) = macx(carry, a[0], b[2]); 72 | let (r3, carry_out) = macx(carry, a[0], b[3]); 73 | 74 | let (r1, carry) = macx(r1, a[1], b[0]); 75 | let (r2, carry) = mac(r2, a[1], b[1], carry); 76 | let (r3, carry) = mac(r3, a[1], b[2], carry); 77 | let (r4, carry_out) = mac(carry_out, a[1], b[3], carry); 78 | 79 | let (r2, carry) = macx(r2, a[2], b[0]); 80 | let (r3, carry) = mac(r3, a[2], b[1], carry); 81 | let (r4, carry) = mac(r4, a[2], b[2], carry); 82 | let (r5, carry_out) = mac(carry_out, a[2], b[3], carry); 83 | 84 | let (r3, carry) = macx(r3, a[3], b[0]); 85 | let (r4, carry) = mac(r4, a[3], b[1], carry); 86 | let (r5, carry) = mac(r5, a[3], b[2], carry); 87 | let (r6, carry_out) = mac(carry_out, a[3], b[3], carry); 88 | 89 | [r0, r1, r2, r3, r4, r5, r6, carry_out] 90 | } 91 | -------------------------------------------------------------------------------- /src/bls12_381/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::mul_512; 2 | use crate::arithmetic::sbb; 3 | use crate::{ 4 | arithmetic::{CurveEndo, EndoParameters}, 5 | endo, 6 | }; 7 | pub use bls12_381::{Fp, G1Projective, Scalar}; 8 | use ff::PrimeField; 9 | use ff::WithSmallOrderMulGroup; 10 | use std::convert::TryInto; 11 | 12 | pub use bls12_381::*; 13 | 14 | // Obtained from https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go 15 | // See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md 16 | // to have more details about the endomorphism. 17 | const ENDO_PARAMS_BLS: EndoParameters = EndoParameters { 18 | // round(b2/n) 19 | gamma2: [0x63f6e522f6cfee30u64, 0x7c6becf1e01faadd, 0x01, 0x0], 20 | // round(-b1/n) 21 | gamma1: [0x02u64, 0x0, 0x0, 0x0], 22 | b1: [0x01u64, 0x0, 0x0, 0x0], 23 | b2: [0x0000000100000000, 0xac45a4010001a402, 0x0, 0x0], 24 | }; 25 | 26 | endo!(G1Projective, Scalar, ENDO_PARAMS_BLS); 27 | 28 | #[test] 29 | fn test_endo() { 30 | use ff::Field; 31 | use rand_core::OsRng; 32 | 33 | for _ in 0..100000 { 34 | let k = Scalar::random(OsRng); 35 | let (k1, k1_neg, k2, k2_neg) = G1Projective::decompose_scalar(&k); 36 | if k1_neg & k2_neg { 37 | assert_eq!( 38 | k, 39 | -Scalar::from_u128(k1) + Scalar::ZETA * Scalar::from_u128(k2) 40 | ) 41 | } else if k1_neg { 42 | assert_eq!( 43 | k, 44 | -Scalar::from_u128(k1) - Scalar::ZETA * Scalar::from_u128(k2) 45 | ) 46 | } else if k2_neg { 47 | assert_eq!( 48 | k, 49 | Scalar::from_u128(k1) + Scalar::ZETA * Scalar::from_u128(k2) 50 | ) 51 | } else { 52 | assert_eq!( 53 | k, 54 | Scalar::from_u128(k1) - Scalar::ZETA * Scalar::from_u128(k2) 55 | ) 56 | } 57 | } 58 | 59 | for _ in 0..100000 { 60 | let k = Scalar::random(OsRng); 61 | let (k1, k1_neg, k2, k2_neg) = G1Projective::decompose_scalar(&k); 62 | if k1_neg & k2_neg { 63 | assert_eq!( 64 | k, 65 | -Scalar::from_u128(k1) + Scalar::ZETA * Scalar::from_u128(k2) 66 | ) 67 | } else if k1_neg { 68 | assert_eq!( 69 | k, 70 | -Scalar::from_u128(k1) - Scalar::ZETA * Scalar::from_u128(k2) 71 | ) 72 | } else if k2_neg { 73 | assert_eq!( 74 | k, 75 | Scalar::from_u128(k1) + Scalar::ZETA * Scalar::from_u128(k2) 76 | ) 77 | } else { 78 | assert_eq!( 79 | k, 80 | Scalar::from_u128(k1) - Scalar::ZETA * Scalar::from_u128(k2) 81 | ) 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/bn256/curve.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::mul_512; 2 | use crate::arithmetic::sbb; 3 | use crate::arithmetic::CurveEndo; 4 | use crate::arithmetic::EndoParameters; 5 | use crate::bn256::Fq; 6 | use crate::bn256::Fq2; 7 | use crate::bn256::Fr; 8 | use crate::endo; 9 | use crate::ff::WithSmallOrderMulGroup; 10 | use crate::ff::{Field, PrimeField}; 11 | use crate::group::Curve; 12 | use crate::group::{cofactor::CofactorGroup, prime::PrimeCurveAffine, Group, GroupEncoding}; 13 | use crate::hash_to_curve::svdw_hash_to_curve; 14 | use crate::{ 15 | batch_add, impl_add_binop_specify_output, impl_binops_additive, 16 | impl_binops_additive_specify_output, impl_binops_multiplicative, 17 | impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, new_curve_impl, 18 | }; 19 | use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt}; 20 | use core::cmp; 21 | use core::fmt::Debug; 22 | use core::iter::Sum; 23 | use core::ops::{Add, Mul, Neg, Sub}; 24 | use rand::RngCore; 25 | use std::convert::TryInto; 26 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 27 | 28 | #[cfg(feature = "derive_serde")] 29 | use serde::{Deserialize, Serialize}; 30 | 31 | new_curve_impl!( 32 | (pub), 33 | G1, 34 | G1Affine, 35 | false, 36 | Fq, 37 | Fr, 38 | (G1_GENERATOR_X,G1_GENERATOR_Y), 39 | G1_A, 40 | G1_B, 41 | "bn256_g1", 42 | |curve_id, domain_prefix| svdw_hash_to_curve(curve_id, domain_prefix, G1::SVDW_Z), 43 | ); 44 | 45 | new_curve_impl!( 46 | (pub), 47 | G2, 48 | G2Affine, 49 | false, 50 | Fq2, 51 | Fr, 52 | (G2_GENERATOR_X, G2_GENERATOR_Y), 53 | G2_A, 54 | G2_B, 55 | "bn256_g2", 56 | |_, _| unimplemented!(), 57 | ); 58 | 59 | impl CurveAffineExt for G1Affine { 60 | batch_add!(); 61 | 62 | fn into_coordinates(self) -> (Self::Base, Self::Base) { 63 | (self.x, self.y) 64 | } 65 | } 66 | 67 | impl CurveAffineExt for G2Affine { 68 | batch_add!(); 69 | 70 | fn into_coordinates(self) -> (Self::Base, Self::Base) { 71 | (self.x, self.y) 72 | } 73 | } 74 | 75 | const G1_GENERATOR_X: Fq = Fq::one(); 76 | const G1_GENERATOR_Y: Fq = Fq::from_raw([2, 0, 0, 0]); 77 | const G1_A: Fq = Fq::from_raw([0, 0, 0, 0]); 78 | const G1_B: Fq = Fq::from_raw([3, 0, 0, 0]); 79 | 80 | const G2_A: Fq2 = Fq2 { 81 | c0: Fq::from_raw([0, 0, 0, 0]), 82 | c1: Fq::from_raw([0, 0, 0, 0]), 83 | }; 84 | 85 | const G2_B: Fq2 = Fq2 { 86 | c0: Fq::from_raw([ 87 | 0x3267e6dc24a138e5, 88 | 0xb5b4c5e559dbefa3, 89 | 0x81be18991be06ac3, 90 | 0x2b149d40ceb8aaae, 91 | ]), 92 | c1: Fq::from_raw([ 93 | 0xe4a2bd0685c315d2, 94 | 0xa74fa084e52d1852, 95 | 0xcd2cafadeed8fdf4, 96 | 0x009713b03af0fed4, 97 | ]), 98 | }; 99 | 100 | const G2_GENERATOR_X: Fq2 = Fq2 { 101 | c0: Fq::from_raw([ 102 | 0x46debd5cd992f6ed, 103 | 0x674322d4f75edadd, 104 | 0x426a00665e5c4479, 105 | 0x1800deef121f1e76, 106 | ]), 107 | c1: Fq::from_raw([ 108 | 0x97e485b7aef312c2, 109 | 0xf1aa493335a9e712, 110 | 0x7260bfb731fb5d25, 111 | 0x198e9393920d483a, 112 | ]), 113 | }; 114 | 115 | const G2_GENERATOR_Y: Fq2 = Fq2 { 116 | c0: Fq::from_raw([ 117 | 0x4ce6cc0166fa7daa, 118 | 0xe3d1e7690c43d37b, 119 | 0x4aab71808dcb408f, 120 | 0x12c85ea5db8c6deb, 121 | ]), 122 | 123 | c1: Fq::from_raw([ 124 | 0x55acdadcd122975b, 125 | 0xbc4b313370b38ef3, 126 | 0xec9e99ad690c3395, 127 | 0x090689d0585ff075, 128 | ]), 129 | }; 130 | 131 | // Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go 132 | // with `bn256::Fr::ZETA` 133 | // See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md 134 | // to have more details about the endomorphism. 135 | const ENDO_PARAMS: EndoParameters = EndoParameters { 136 | // round(b2/n) 137 | gamma1: [ 138 | 0x7a7bd9d4391eb18du64, 139 | 0x4ccef014a773d2cfu64, 140 | 0x0000000000000002u64, 141 | 0u64, 142 | ], 143 | // round(-b1/n) 144 | gamma2: [0xd91d232ec7e0b3d7u64, 0x0000000000000002u64, 0u64, 0u64], 145 | b1: [0x8211bbeb7d4f1128u64, 0x6f4d8248eeb859fcu64, 0u64, 0u64], 146 | b2: [0x89d3256894d213e3u64, 0u64, 0u64, 0u64], 147 | }; 148 | 149 | endo!(G1, Fr, ENDO_PARAMS); 150 | 151 | impl group::cofactor::CofactorGroup for G1 { 152 | type Subgroup = G1; 153 | 154 | fn clear_cofactor(&self) -> Self { 155 | *self 156 | } 157 | 158 | fn into_subgroup(self) -> CtOption { 159 | CtOption::new(self, 1.into()) 160 | } 161 | 162 | fn is_torsion_free(&self) -> Choice { 163 | 1.into() 164 | } 165 | } 166 | 167 | impl CofactorGroup for G2 { 168 | type Subgroup = G2; 169 | 170 | fn clear_cofactor(&self) -> Self { 171 | // "0x30644e72e131a029b85045b68181585e06ceecda572a2489345f2299c0f9fa8d" 172 | let e: [u8; 32] = [ 173 | 0x30, 0x64, 0x4e, 0x72, 0xe1, 0x31, 0xa0, 0x29, 0xb8, 0x50, 0x45, 0xb6, 0x81, 0x81, 174 | 0x58, 0x5e, 0x06, 0xce, 0xec, 0xda, 0x57, 0x2a, 0x24, 0x89, 0x34, 0x5f, 0x22, 0x99, 175 | 0xc0, 0xf9, 0xfa, 0x8d, 176 | ]; 177 | 178 | // self * COFACTOR_G2 179 | let mut acc = G2::identity(); 180 | for bit in e 181 | .iter() 182 | .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) 183 | .skip(1) 184 | { 185 | acc = acc.double(); 186 | acc = G2::conditional_select(&acc, &(acc + self), bit); 187 | } 188 | acc 189 | } 190 | 191 | fn into_subgroup(self) -> CtOption { 192 | unimplemented!(); 193 | } 194 | 195 | fn is_torsion_free(&self) -> Choice { 196 | // "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001" 197 | let e: [u8; 32] = [ 198 | 0x30, 0x64, 0x4e, 0x72, 0xe1, 0x31, 0xa0, 0x29, 0xb8, 0x50, 0x45, 0xb6, 0x81, 0x81, 199 | 0x58, 0x5d, 0x28, 0x33, 0xe8, 0x48, 0x79, 0xb9, 0x70, 0x91, 0x43, 0xe1, 0xf5, 0x93, 200 | 0xf0, 0x00, 0x00, 0x01, 201 | ]; 202 | 203 | // self * GROUP_ORDER; 204 | 205 | let mut acc = G2::identity(); 206 | for bit in e 207 | .iter() 208 | .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) 209 | .skip(1) 210 | { 211 | acc = acc.double(); 212 | acc = G2::conditional_select(&acc, &(acc + self), bit); 213 | } 214 | acc.is_identity() 215 | } 216 | } 217 | 218 | impl G1 { 219 | const SVDW_Z: Fq = Fq::ONE; 220 | } 221 | 222 | #[cfg(test)] 223 | mod tests { 224 | use crate::arithmetic::CurveEndo; 225 | use crate::bn256::{Fr, G1, G2}; 226 | use crate::CurveExt; 227 | use ff::Field; 228 | use ff::{PrimeField, WithSmallOrderMulGroup}; 229 | use rand_core::OsRng; 230 | 231 | #[test] 232 | fn test_hash_to_curve() { 233 | crate::tests::curve::hash_to_curve_test::(); 234 | } 235 | 236 | #[test] 237 | fn test_map_to_curve() { 238 | crate::tests::curve::svdw_map_to_curve_test::( 239 | G1::SVDW_Z, 240 | // Precomputed constants taken from https://github.com/ConsenSys/gnark-crypto/blob/441dc0ffe639294b8d09e394f24ba7575577229c/internal/generator/config/bn254.go#L26-L32. 241 | [ 242 | "4", 243 | "10944121435919637611123202872628637544348155578648911831344518947322613104291", 244 | "8815841940592487685674414971303048083897117035520822607866", 245 | "7296080957279758407415468581752425029565437052432607887563012631548408736189", 246 | ], 247 | // List of (u, (Q.x, Q.y)) taken from https://github.com/ConsenSys/gnark-crypto/blob/441dc0ffe639294b8d09e394f24ba7575577229c/ecc/bn254/hash_vectors_test.go#L4-L28 248 | [ 249 | ( 250 | "0xcb81538a98a2e3580076eed495256611813f6dae9e16d3d4f8de7af0e9833e1", 251 | ( 252 | "0x1bb8810e2ceaf04786d4efd216fc2820ddd9363712efc736ada11049d8af5925", 253 | "0x1efbf8d54c60d865cce08437668ea30f5bf90d287dbd9b5af31da852915e8f11", 254 | ), 255 | ), 256 | ( 257 | "0xba35e127276e9000b33011860904ddee28f1d48ddd3577e2a797ef4a5e62319", 258 | ( 259 | "0xda4a96147df1f35b0f820bd35c6fac3b80e8e320de7c536b1e054667b22c332", 260 | "0x189bd3fbffe4c8740d6543754d95c790e44cd2d162858e3b733d2b8387983bb7", 261 | ), 262 | ), 263 | ( 264 | "0x11852286660cd970e9d7f46f99c7cca2b75554245e91b9b19d537aa6147c28fc", 265 | ( 266 | "0x2ff727cfaaadb3acab713fa22d91f5fddab3ed77948f3ef6233d7ea9b03f4da1", 267 | "0x304080768fd2f87a852155b727f97db84b191e41970506f0326ed4046d1141aa", 268 | ), 269 | ), 270 | ( 271 | "0x174d1c85d8a690a876cc1deba0166d30569fafdb49cb3ed28405bd1c5357a1cc", 272 | ( 273 | "0x11a2eaa8e3e89de056d1b3a288a7f733c8a1282efa41d28e71af065ab245df9b", 274 | "0x60f37c447ac29fd97b9bb83be98ddccf15e34831a9cdf5493b7fede0777ae06", 275 | ), 276 | ), 277 | ( 278 | "0x73b81432b4cf3a8a9076201500d1b94159539f052a6e0928db7f2df74bff672", 279 | ( 280 | "0x27409dccc6ee4ce90e24744fda8d72c0bc64e79766f778da0c1c0ef1c186ea84", 281 | "0x1ac201a542feca15e77f30370da183514dc99d8a0b2c136d64ede35cd0b51dc0", 282 | ), 283 | ), 284 | ], 285 | ); 286 | } 287 | 288 | #[test] 289 | fn test_curve() { 290 | crate::tests::curve::curve_tests::(); 291 | crate::tests::curve::curve_tests::(); 292 | } 293 | 294 | #[test] 295 | fn test_endo() { 296 | let g = G1::generator(); 297 | assert_eq!(g * Fr::ZETA, g.endo()); 298 | let g = G2::generator(); 299 | assert_eq!(g * Fr::ZETA, g.endo()); 300 | for _ in 0..100000 { 301 | let k = Fr::random(OsRng); 302 | let (k1, k1_neg, k2, k2_neg) = G1::decompose_scalar(&k); 303 | if k1_neg & k2_neg { 304 | assert_eq!(k, -Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) 305 | } else if k1_neg { 306 | assert_eq!(k, -Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) 307 | } else if k2_neg { 308 | assert_eq!(k, Fr::from_u128(k1) + Fr::ZETA * Fr::from_u128(k2)) 309 | } else { 310 | assert_eq!(k, Fr::from_u128(k1) - Fr::ZETA * Fr::from_u128(k2)) 311 | } 312 | } 313 | } 314 | 315 | #[test] 316 | fn test_serialization() { 317 | crate::tests::curve::random_serialization_test::(); 318 | crate::tests::curve::random_serialization_test::(); 319 | #[cfg(feature = "derive_serde")] 320 | { 321 | crate::tests::curve::random_serde_test::(); 322 | crate::tests::curve::random_serde_test::(); 323 | } 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /src/bn256/fq.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "asm")] 2 | use crate::bn256::assembly::field_arithmetic_asm; 3 | #[cfg(not(feature = "asm"))] 4 | use crate::{field_arithmetic, field_specific}; 5 | 6 | use crate::arithmetic::{adc, mac, sbb}; 7 | use crate::bn256::LegendreSymbol; 8 | use crate::ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; 9 | use crate::{ 10 | field_bits, field_common, impl_add_binop_specify_output, impl_binops_additive, 11 | impl_binops_additive_specify_output, impl_binops_multiplicative, 12 | impl_binops_multiplicative_mixed, impl_from_u64, impl_sub_binop_specify_output, impl_sum_prod, 13 | }; 14 | use core::convert::TryInto; 15 | use core::fmt; 16 | use core::ops::{Add, Mul, Neg, Sub}; 17 | use rand::RngCore; 18 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 19 | 20 | #[cfg(feature = "derive_serde")] 21 | use serde::{Deserialize, Serialize}; 22 | 23 | /// This represents an element of $\mathbb{F}_q$ where 24 | /// 25 | /// `p = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47` 26 | /// 27 | /// is the base field of the BN254 curve. 28 | // The internal representation of this type is four 64-bit unsigned 29 | // integers in little-endian order. `Fq` values are always in 30 | // Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256. 31 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 32 | #[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] 33 | pub struct Fq(pub(crate) [u64; 4]); 34 | 35 | /// Constant representing the modulus 36 | /// q = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47 37 | const MODULUS: Fq = Fq([ 38 | 0x3c208c16d87cfd47, 39 | 0x97816a916871ca8d, 40 | 0xb85045b68181585d, 41 | 0x30644e72e131a029, 42 | ]); 43 | 44 | /// The modulus as u32 limbs. 45 | #[cfg(not(target_pointer_width = "64"))] 46 | const MODULUS_LIMBS_32: [u32; 8] = [ 47 | 0xd87c_fd47, 48 | 0x3c20_8c16, 49 | 0x6871_ca8d, 50 | 0x9781_6a91, 51 | 0x8181_585d, 52 | 0xb850_45b6, 53 | 0xe131_a029, 54 | 0x3064_4e72, 55 | ]; 56 | 57 | /// INV = -(q^{-1} mod 2^64) mod 2^64 58 | const INV: u64 = 0x87d20782e4866389; 59 | 60 | /// R = 2^256 mod q 61 | const R: Fq = Fq([ 62 | 0xd35d438dc58f0d9d, 63 | 0x0a78eb28f5c70b3d, 64 | 0x666ea36f7879462c, 65 | 0x0e0a77c19a07df2f, 66 | ]); 67 | 68 | /// R^2 = 2^512 mod q 69 | const R2: Fq = Fq([ 70 | 0xf32cfc5b538afa89, 71 | 0xb5e71911d44501fb, 72 | 0x47ab1eff0a417ff6, 73 | 0x06d89f71cab8351f, 74 | ]); 75 | 76 | /// R^3 = 2^768 mod q 77 | const R3: Fq = Fq([ 78 | 0xb1cd6dafda1530df, 79 | 0x62f210e6a7283db6, 80 | 0xef7f0b0c0ada0afb, 81 | 0x20fd6e902d592544, 82 | ]); 83 | 84 | pub const NEGATIVE_ONE: Fq = Fq([ 85 | 0x68c3488912edefaa, 86 | 0x8d087f6872aabf4f, 87 | 0x51e1a24709081231, 88 | 0x2259d6b14729c0fa, 89 | ]); 90 | 91 | const MODULUS_STR: &str = "0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47"; 92 | 93 | /// Obtained with: 94 | /// `sage: GF(0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47).primitive_element()` 95 | const MULTIPLICATIVE_GENERATOR: Fq = Fq::from_raw([0x03, 0x0, 0x0, 0x0]); 96 | 97 | const TWO_INV: Fq = Fq::from_raw([ 98 | 0x9e10460b6c3e7ea4, 99 | 0xcbc0b548b438e546, 100 | 0xdc2822db40c0ac2e, 101 | 0x183227397098d014, 102 | ]); 103 | 104 | /// `0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46` 105 | const ROOT_OF_UNITY: Fq = Fq::from_raw([ 106 | 0x3c208c16d87cfd46, 107 | 0x97816a916871ca8d, 108 | 0xb85045b68181585d, 109 | 0x30644e72e131a029, 110 | ]); 111 | 112 | /// `0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46` 113 | const ROOT_OF_UNITY_INV: Fq = Fq::from_raw([ 114 | 0x3c208c16d87cfd46, 115 | 0x97816a916871ca8d, 116 | 0xb85045b68181585d, 117 | 0x30644e72e131a029, 118 | ]); 119 | 120 | // `0x9` 121 | const DELTA: Fq = Fq::from_raw([0x9, 0, 0, 0]); 122 | 123 | /// `ZETA^3 = 1 mod r` where `ZETA^2 != 1 mod r` 124 | const ZETA: Fq = Fq::from_raw([ 125 | 0x5763473177fffffeu64, 126 | 0xd4f263f1acdb5c4fu64, 127 | 0x59e26bcea0d48bacu64, 128 | 0x0u64, 129 | ]); 130 | 131 | impl_binops_additive!(Fq, Fq); 132 | impl_binops_multiplicative!(Fq, Fq); 133 | field_common!( 134 | Fq, 135 | MODULUS, 136 | INV, 137 | MODULUS_STR, 138 | TWO_INV, 139 | ROOT_OF_UNITY_INV, 140 | DELTA, 141 | ZETA, 142 | R, 143 | R2, 144 | R3 145 | ); 146 | impl_sum_prod!(Fq); 147 | impl_from_u64!(Fq, R2); 148 | 149 | #[cfg(not(feature = "asm"))] 150 | field_arithmetic!(Fq, MODULUS, INV, sparse); 151 | #[cfg(feature = "asm")] 152 | field_arithmetic_asm!(Fq, MODULUS, INV); 153 | 154 | #[cfg(target_pointer_width = "64")] 155 | field_bits!(Fq, MODULUS); 156 | #[cfg(not(target_pointer_width = "64"))] 157 | field_bits!(Fq, MODULUS, MODULUS_LIMBS_32); 158 | 159 | impl Fq { 160 | pub const fn size() -> usize { 161 | 32 162 | } 163 | 164 | pub fn legendre(&self) -> LegendreSymbol { 165 | // s = self^((modulus - 1) // 2) 166 | // 0x183227397098d014dc2822db40c0ac2ecbc0b548b438e5469e10460b6c3e7ea3 167 | let s = &[ 168 | 0x9e10460b6c3e7ea3u64, 169 | 0xcbc0b548b438e546u64, 170 | 0xdc2822db40c0ac2eu64, 171 | 0x183227397098d014u64, 172 | ]; 173 | let s = self.pow(s); 174 | if s == Self::zero() { 175 | LegendreSymbol::Zero 176 | } else if s == Self::one() { 177 | LegendreSymbol::QuadraticResidue 178 | } else { 179 | LegendreSymbol::QuadraticNonResidue 180 | } 181 | } 182 | } 183 | 184 | impl ff::Field for Fq { 185 | const ZERO: Self = Self::zero(); 186 | const ONE: Self = Self::one(); 187 | 188 | fn random(mut rng: impl RngCore) -> Self { 189 | let mut random_bytes = [0; 64]; 190 | rng.fill_bytes(&mut random_bytes[..]); 191 | 192 | Self::from_uniform_bytes(&random_bytes) 193 | } 194 | 195 | fn double(&self) -> Self { 196 | self.double() 197 | } 198 | 199 | #[inline(always)] 200 | fn square(&self) -> Self { 201 | self.square() 202 | } 203 | 204 | /// Computes the square root of this element, if it exists. 205 | fn sqrt(&self) -> CtOption { 206 | let tmp = self.pow([ 207 | 0x4f082305b61f3f52, 208 | 0x65e05aa45a1c72a3, 209 | 0x6e14116da0605617, 210 | 0x0c19139cb84c680a, 211 | ]); 212 | 213 | CtOption::new(tmp, tmp.square().ct_eq(self)) 214 | } 215 | 216 | fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { 217 | ff::helpers::sqrt_ratio_generic(num, div) 218 | } 219 | 220 | /// Returns the multiplicative inverse of the 221 | /// element. If it is zero, the method fails. 222 | fn invert(&self) -> CtOption { 223 | self.invert() 224 | } 225 | } 226 | 227 | impl ff::PrimeField for Fq { 228 | type Repr = [u8; 32]; 229 | 230 | const NUM_BITS: u32 = 254; 231 | const CAPACITY: u32 = 253; 232 | const MODULUS: &'static str = MODULUS_STR; 233 | const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; 234 | const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; 235 | const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; 236 | const TWO_INV: Self = TWO_INV; 237 | const DELTA: Self = DELTA; 238 | const S: u32 = 0; 239 | 240 | fn from_repr(repr: Self::Repr) -> CtOption { 241 | let mut tmp = Fq([0, 0, 0, 0]); 242 | 243 | tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); 244 | tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); 245 | tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); 246 | tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); 247 | 248 | // Try to subtract the modulus 249 | let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); 250 | let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); 251 | let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); 252 | let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); 253 | 254 | // If the element is smaller than MODULUS then the 255 | // subtraction will underflow, producing a borrow value 256 | // of 0xffff...ffff. Otherwise, it'll be zero. 257 | let is_some = (borrow as u8) & 1; 258 | 259 | // Convert to Montgomery form by computing 260 | // (a.R^0 * R^2) / R = a.R 261 | tmp *= &R2; 262 | 263 | CtOption::new(tmp, Choice::from(is_some)) 264 | } 265 | 266 | fn to_repr(&self) -> Self::Repr { 267 | // Turn into canonical form by computing 268 | // (a.R) / R = a 269 | let tmp = 270 | Self::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); 271 | 272 | let mut res = [0; 32]; 273 | res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); 274 | res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); 275 | res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); 276 | res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); 277 | 278 | res 279 | } 280 | 281 | fn is_odd(&self) -> Choice { 282 | Choice::from(self.to_repr()[0] & 1) 283 | } 284 | } 285 | 286 | impl FromUniformBytes<64> for Fq { 287 | /// Converts a 512-bit little endian integer into 288 | /// an `Fq` by reducing by the modulus. 289 | fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { 290 | Self::from_u512([ 291 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 292 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 293 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 294 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 295 | u64::from_le_bytes(bytes[32..40].try_into().unwrap()), 296 | u64::from_le_bytes(bytes[40..48].try_into().unwrap()), 297 | u64::from_le_bytes(bytes[48..56].try_into().unwrap()), 298 | u64::from_le_bytes(bytes[56..64].try_into().unwrap()), 299 | ]) 300 | } 301 | } 302 | 303 | impl WithSmallOrderMulGroup<3> for Fq { 304 | const ZETA: Self = ZETA; 305 | } 306 | 307 | #[cfg(test)] 308 | mod test { 309 | use super::*; 310 | use ff::Field; 311 | use rand_core::OsRng; 312 | 313 | #[test] 314 | fn test_sqrt_fq() { 315 | let v = (Fq::TWO_INV).square().sqrt().unwrap(); 316 | assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV); 317 | 318 | for _ in 0..10000 { 319 | let a = Fq::random(OsRng); 320 | let mut b = a; 321 | b = b.square(); 322 | assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); 323 | 324 | let b = b.sqrt().unwrap(); 325 | let mut negb = b; 326 | negb = negb.neg(); 327 | 328 | assert!(a == b || a == negb); 329 | } 330 | 331 | let mut c = Fq::one(); 332 | for _ in 0..10000 { 333 | let mut b = c; 334 | b = b.square(); 335 | assert_eq!(b.legendre(), LegendreSymbol::QuadraticResidue); 336 | 337 | b = b.sqrt().unwrap(); 338 | 339 | if b != c { 340 | b = b.neg(); 341 | } 342 | 343 | assert_eq!(b, c); 344 | 345 | c += &Fq::one(); 346 | } 347 | } 348 | 349 | #[test] 350 | fn test_from_u512() { 351 | assert_eq!( 352 | Fq::from_raw([ 353 | 0x1f8905a172affa8a, 354 | 0xde45ad177dcf3306, 355 | 0xaaa7987907d73ae2, 356 | 0x24d349431d468e30, 357 | ]), 358 | Fq::from_u512([ 359 | 0xaaaaaaaaaaaaaaaa, 360 | 0xaaaaaaaaaaaaaaaa, 361 | 0xaaaaaaaaaaaaaaaa, 362 | 0xaaaaaaaaaaaaaaaa, 363 | 0xaaaaaaaaaaaaaaaa, 364 | 0xaaaaaaaaaaaaaaaa, 365 | 0xaaaaaaaaaaaaaaaa, 366 | 0xaaaaaaaaaaaaaaaa 367 | ]) 368 | ); 369 | } 370 | 371 | #[test] 372 | fn test_field() { 373 | crate::tests::field::random_field_tests::("fq".to_string()); 374 | } 375 | 376 | #[test] 377 | #[cfg(feature = "bits")] 378 | fn test_bits() { 379 | crate::tests::field::random_bits_tests::("fq".to_string()); 380 | } 381 | 382 | #[test] 383 | fn test_serialization() { 384 | crate::tests::field::random_serialization_test::("fq".to_string()); 385 | #[cfg(feature = "derive_serde")] 386 | crate::tests::field::random_serde_test::("fq".to_string()); 387 | } 388 | } 389 | -------------------------------------------------------------------------------- /src/bn256/fq12.rs: -------------------------------------------------------------------------------- 1 | use super::fq::Fq; 2 | use super::fq2::Fq2; 3 | use super::fq6::Fq6; 4 | use crate::ff::Field; 5 | use core::ops::{Add, Mul, Neg, Sub}; 6 | use rand::RngCore; 7 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 8 | 9 | #[derive(Copy, Clone, Debug, Eq, PartialEq, Default)] 10 | pub struct Fq12 { 11 | pub c0: Fq6, 12 | pub c1: Fq6, 13 | } 14 | 15 | impl ConditionallySelectable for Fq12 { 16 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 17 | Fq12 { 18 | c0: Fq6::conditional_select(&a.c0, &b.c0, choice), 19 | c1: Fq6::conditional_select(&a.c1, &b.c1, choice), 20 | } 21 | } 22 | } 23 | 24 | impl ConstantTimeEq for Fq12 { 25 | fn ct_eq(&self, other: &Self) -> Choice { 26 | self.c0.ct_eq(&other.c0) & self.c1.ct_eq(&other.c1) 27 | } 28 | } 29 | 30 | impl Neg for Fq12 { 31 | type Output = Fq12; 32 | 33 | #[inline] 34 | fn neg(self) -> Fq12 { 35 | -&self 36 | } 37 | } 38 | 39 | impl<'a> Neg for &'a Fq12 { 40 | type Output = Fq12; 41 | 42 | #[inline] 43 | fn neg(self) -> Fq12 { 44 | self.neg() 45 | } 46 | } 47 | 48 | impl<'a, 'b> Sub<&'b Fq12> for &'a Fq12 { 49 | type Output = Fq12; 50 | 51 | #[inline] 52 | fn sub(self, rhs: &'b Fq12) -> Fq12 { 53 | self.sub(rhs) 54 | } 55 | } 56 | 57 | impl<'a, 'b> Add<&'b Fq12> for &'a Fq12 { 58 | type Output = Fq12; 59 | 60 | #[inline] 61 | fn add(self, rhs: &'b Fq12) -> Fq12 { 62 | self.add(rhs) 63 | } 64 | } 65 | 66 | impl<'a, 'b> Mul<&'b Fq12> for &'a Fq12 { 67 | type Output = Fq12; 68 | 69 | #[inline] 70 | fn mul(self, rhs: &'b Fq12) -> Fq12 { 71 | self.mul(rhs) 72 | } 73 | } 74 | 75 | use crate::{ 76 | impl_add_binop_specify_output, impl_binops_additive, impl_binops_additive_specify_output, 77 | impl_binops_multiplicative, impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, 78 | impl_sum_prod, 79 | }; 80 | impl_binops_additive!(Fq12, Fq12); 81 | impl_binops_multiplicative!(Fq12, Fq12); 82 | impl_sum_prod!(Fq12); 83 | 84 | impl Fq12 { 85 | #[inline] 86 | pub const fn zero() -> Self { 87 | Fq12 { 88 | c0: Fq6::ZERO, 89 | c1: Fq6::ZERO, 90 | } 91 | } 92 | 93 | #[inline] 94 | pub const fn one() -> Self { 95 | Fq12 { 96 | c0: Fq6::ONE, 97 | c1: Fq6::ZERO, 98 | } 99 | } 100 | 101 | pub fn mul_assign(&mut self, other: &Self) { 102 | let t0 = self.c0 * other.c0; 103 | let mut t1 = self.c1 * other.c1; 104 | let t2 = other.c0 + other.c1; 105 | 106 | self.c1 += &self.c0; 107 | self.c1 *= &t2; 108 | self.c1 -= &t0; 109 | self.c1 -= &t1; 110 | 111 | t1.mul_by_nonresidue(); 112 | self.c0 = t0 + t1; 113 | } 114 | 115 | pub fn square_assign(&mut self) { 116 | let mut ab = self.c0 * self.c1; 117 | 118 | let c0c1 = self.c0 + self.c1; 119 | 120 | let mut c0 = self.c1; 121 | c0.mul_by_nonresidue(); 122 | c0 += &self.c0; 123 | c0 *= &c0c1; 124 | c0 -= &ab; 125 | self.c1 = ab; 126 | self.c1 += &ab; 127 | ab.mul_by_nonresidue(); 128 | c0 -= &ab; 129 | self.c0 = c0; 130 | } 131 | 132 | pub fn double(&self) -> Self { 133 | Self { 134 | c0: self.c0.double(), 135 | c1: self.c1.double(), 136 | } 137 | } 138 | 139 | pub fn double_assign(&mut self) { 140 | self.c0 = self.c0.double(); 141 | self.c1 = self.c1.double(); 142 | } 143 | 144 | pub fn add(&self, other: &Self) -> Self { 145 | Self { 146 | c0: self.c0 + other.c0, 147 | c1: self.c1 + other.c1, 148 | } 149 | } 150 | 151 | pub fn sub(&self, other: &Self) -> Self { 152 | Self { 153 | c0: self.c0 - other.c0, 154 | c1: self.c1 - other.c1, 155 | } 156 | } 157 | 158 | pub fn mul(&self, other: &Self) -> Self { 159 | let mut t = *other; 160 | t.mul_assign(self); 161 | t 162 | } 163 | 164 | pub fn square(&self) -> Self { 165 | let mut t = *self; 166 | t.square_assign(); 167 | t 168 | } 169 | 170 | #[inline(always)] 171 | pub fn neg(&self) -> Self { 172 | Self { 173 | c0: -self.c0, 174 | c1: -self.c1, 175 | } 176 | } 177 | 178 | #[inline(always)] 179 | pub fn conjugate(&mut self) { 180 | self.c1 = -self.c1; 181 | } 182 | 183 | // pub fn conjugate(&self) -> Self { 184 | // Self { 185 | // c0: self.c0, 186 | // c1: -self.c1, 187 | // } 188 | // } 189 | 190 | pub fn frobenius_map(&mut self, power: usize) { 191 | self.c0.frobenius_map(power); 192 | self.c1.frobenius_map(power); 193 | 194 | self.c1.c0.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); 195 | self.c1.c1.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); 196 | self.c1.c2.mul_assign(&FROBENIUS_COEFF_FQ12_C1[power % 12]); 197 | } 198 | 199 | pub fn mul_by_014(&mut self, c0: &Fq2, c1: &Fq2, c4: &Fq2) { 200 | let mut aa = self.c0; 201 | aa.mul_by_01(c0, c1); 202 | let mut bb = self.c1; 203 | bb.mul_by_1(c4); 204 | let o = c1 + c4; 205 | self.c1 += &self.c0; 206 | self.c1.mul_by_01(c0, &o); 207 | self.c1 -= &aa; 208 | self.c1 -= &bb; 209 | self.c0 = bb; 210 | self.c0.mul_by_nonresidue(); 211 | self.c0 += &aa; 212 | } 213 | 214 | pub fn mul_by_034(&mut self, c0: &Fq2, c3: &Fq2, c4: &Fq2) { 215 | let t0 = Fq6 { 216 | c0: self.c0.c0 * c0, 217 | c1: self.c0.c1 * c0, 218 | c2: self.c0.c2 * c0, 219 | }; 220 | let mut t1 = self.c1; 221 | t1.mul_by_01(c3, c4); 222 | let o = c0 + c3; 223 | let mut t2 = self.c0 + self.c1; 224 | t2.mul_by_01(&o, c4); 225 | t2 -= t0; 226 | self.c1 = t2 - t1; 227 | t1.mul_by_nonresidue(); 228 | self.c0 = t0 + t1; 229 | } 230 | 231 | pub fn invert(&self) -> CtOption { 232 | let mut c0s = self.c0; 233 | c0s.square_assign(); 234 | let mut c1s = self.c1; 235 | c1s.square_assign(); 236 | c1s.mul_by_nonresidue(); 237 | c0s -= &c1s; 238 | 239 | c0s.invert().map(|t| { 240 | let mut tmp = Fq12 { c0: t, c1: t }; 241 | tmp.c0.mul_assign(&self.c0); 242 | tmp.c1.mul_assign(&self.c1); 243 | tmp.c1 = tmp.c1.neg(); 244 | 245 | tmp 246 | }) 247 | } 248 | 249 | pub fn cyclotomic_square(&mut self) { 250 | fn fp4_square(c0: &mut Fq2, c1: &mut Fq2, a0: &Fq2, a1: &Fq2) { 251 | let t0 = a0.square(); 252 | let t1 = a1.square(); 253 | let mut t2 = t1; 254 | t2.mul_by_nonresidue(); 255 | *c0 = t2 + t0; 256 | t2 = a0 + a1; 257 | t2.square_assign(); 258 | t2 -= t0; 259 | *c1 = t2 - t1; 260 | } 261 | 262 | let mut t3 = Fq2::zero(); 263 | let mut t4 = Fq2::zero(); 264 | let mut t5 = Fq2::zero(); 265 | let mut t6 = Fq2::zero(); 266 | 267 | fp4_square(&mut t3, &mut t4, &self.c0.c0, &self.c1.c1); 268 | let mut t2 = t3 - self.c0.c0; 269 | t2.double_assign(); 270 | self.c0.c0 = t2 + t3; 271 | 272 | t2 = t4 + self.c1.c1; 273 | t2.double_assign(); 274 | self.c1.c1 = t2 + t4; 275 | 276 | fp4_square(&mut t3, &mut t4, &self.c1.c0, &self.c0.c2); 277 | fp4_square(&mut t5, &mut t6, &self.c0.c1, &self.c1.c2); 278 | 279 | t2 = t3 - self.c0.c1; 280 | t2.double_assign(); 281 | self.c0.c1 = t2 + t3; 282 | t2 = t4 + self.c1.c2; 283 | t2.double_assign(); 284 | self.c1.c2 = t2 + t4; 285 | t3 = t6; 286 | t3.mul_by_nonresidue(); 287 | t2 = t3 + self.c1.c0; 288 | t2.double_assign(); 289 | self.c1.c0 = t2 + t3; 290 | t2 = t5 - self.c0.c2; 291 | t2.double_assign(); 292 | self.c0.c2 = t2 + t5; 293 | } 294 | } 295 | 296 | impl Field for Fq12 { 297 | const ZERO: Self = Self::zero(); 298 | const ONE: Self = Self::one(); 299 | 300 | fn random(mut rng: impl RngCore) -> Self { 301 | Fq12 { 302 | c0: Fq6::random(&mut rng), 303 | c1: Fq6::random(&mut rng), 304 | } 305 | } 306 | 307 | fn is_zero(&self) -> Choice { 308 | self.c0.is_zero() & self.c1.is_zero() 309 | } 310 | 311 | fn square(&self) -> Self { 312 | self.square() 313 | } 314 | 315 | fn double(&self) -> Self { 316 | self.double() 317 | } 318 | 319 | fn sqrt(&self) -> CtOption { 320 | unimplemented!() 321 | } 322 | 323 | fn sqrt_ratio(_num: &Self, _div: &Self) -> (Choice, Self) { 324 | unimplemented!() 325 | } 326 | 327 | fn invert(&self) -> CtOption { 328 | self.invert() 329 | } 330 | } 331 | 332 | // non_residue^((modulus^i-1)/6) for i=0,...,11 333 | pub const FROBENIUS_COEFF_FQ12_C1: [Fq2; 12] = [ 334 | // Fq2(u + 9)**(((q^0) - 1) / 6) 335 | // Fq points are represented in Montgomery form with R = 2^256 336 | Fq2 { 337 | c0: Fq([ 338 | 0xd35d438dc58f0d9d, 339 | 0x0a78eb28f5c70b3d, 340 | 0x666ea36f7879462c, 341 | 0x0e0a77c19a07df2f, 342 | ]), 343 | c1: Fq([0x0, 0x0, 0x0, 0x0]), 344 | }, 345 | // Fq2(u + 9)**(((q^1) - 1) / 6) 346 | Fq2 { 347 | c0: Fq([ 348 | 0xaf9ba69633144907, 349 | 0xca6b1d7387afb78a, 350 | 0x11bded5ef08a2087, 351 | 0x02f34d751a1f3a7c, 352 | ]), 353 | c1: Fq([ 354 | 0xa222ae234c492d72, 355 | 0xd00f02a4565de15b, 356 | 0xdc2ff3a253dfc926, 357 | 0x10a75716b3899551, 358 | ]), 359 | }, 360 | // Fq2(u + 9)**(((q^2) - 1) / 6) 361 | Fq2 { 362 | c0: Fq([ 363 | 0xca8d800500fa1bf2, 364 | 0xf0c5d61468b39769, 365 | 0x0e201271ad0d4418, 366 | 0x04290f65bad856e6, 367 | ]), 368 | c1: Fq([0x0, 0x0, 0x0, 0x0]), 369 | }, 370 | // Fq2(u + 9)**(((q^3) - 1) / 6) 371 | Fq2 { 372 | c0: Fq([ 373 | 0x365316184e46d97d, 374 | 0x0af7129ed4c96d9f, 375 | 0x659da72fca1009b5, 376 | 0x08116d8983a20d23, 377 | ]), 378 | c1: Fq([ 379 | 0xb1df4af7c39c1939, 380 | 0x3d9f02878a73bf7f, 381 | 0x9b2220928caf0ae0, 382 | 0x26684515eff054a6, 383 | ]), 384 | }, 385 | // Fq2(u + 9)**(((q^4) - 1) / 6) 386 | Fq2 { 387 | c0: Fq([ 388 | 0x3350c88e13e80b9c, 389 | 0x7dce557cdb5e56b9, 390 | 0x6001b4b8b615564a, 391 | 0x2682e617020217e0, 392 | ]), 393 | c1: Fq([0x0, 0x0, 0x0, 0x0]), 394 | }, 395 | // Fq2(u + 9)**(((q^5) - 1) / 6) 396 | Fq2 { 397 | c0: Fq([ 398 | 0x86b76f821b329076, 399 | 0x408bf52b4d19b614, 400 | 0x53dfb9d0d985e92d, 401 | 0x051e20146982d2a7, 402 | ]), 403 | c1: Fq([ 404 | 0x0fbc9cd47752ebc7, 405 | 0x6d8fffe33415de24, 406 | 0xbef22cf038cf41b9, 407 | 0x15c0edff3c66bf54, 408 | ]), 409 | }, 410 | // Fq2(u + 9)**(((q^6) - 1) / 6) 411 | Fq2 { 412 | c0: Fq([ 413 | 0x68c3488912edefaa, 414 | 0x8d087f6872aabf4f, 415 | 0x51e1a24709081231, 416 | 0x2259d6b14729c0fa, 417 | ]), 418 | c1: Fq([0x0, 0x0, 0x0, 0x0]), 419 | }, 420 | // Fq2(u + 9)**(((q^7) - 1) / 6) 421 | Fq2 { 422 | c0: Fq([ 423 | 0x8c84e580a568b440, 424 | 0xcd164d1de0c21302, 425 | 0xa692585790f737d5, 426 | 0x2d7100fdc71265ad, 427 | ]), 428 | c1: Fq([ 429 | 0x99fdddf38c33cfd5, 430 | 0xc77267ed1213e931, 431 | 0xdc2052142da18f36, 432 | 0x1fbcf75c2da80ad7, 433 | ]), 434 | }, 435 | // Fq2(u + 9)**(((q^8) - 1) / 6) 436 | Fq2 { 437 | c0: Fq([ 438 | 0x71930c11d782e155, 439 | 0xa6bb947cffbe3323, 440 | 0xaa303344d4741444, 441 | 0x2c3b3f0d26594943, 442 | ]), 443 | c1: Fq([0x0, 0x0, 0x0, 0x0]), 444 | }, 445 | // Fq2(u + 9)**(((q^9) - 1) / 6) 446 | Fq2 { 447 | c0: Fq([ 448 | 0x05cd75fe8a3623ca, 449 | 0x8c8a57f293a85cee, 450 | 0x52b29e86b7714ea8, 451 | 0x2852e0e95d8f9306, 452 | ]), 453 | c1: Fq([ 454 | 0x8a41411f14e0e40e, 455 | 0x59e26809ddfe0b0d, 456 | 0x1d2e2523f4d24d7d, 457 | 0x09fc095cf1414b83, 458 | ]), 459 | }, 460 | // Fq2(u + 9)**(((q^10) - 1) / 6) 461 | Fq2 { 462 | c0: Fq([ 463 | 0x08cfc388c494f1ab, 464 | 0x19b315148d1373d4, 465 | 0x584e90fdcb6c0213, 466 | 0x09e1685bdf2f8849, 467 | ]), 468 | c1: Fq([0x0, 0x0, 0x0, 0x0]), 469 | }, 470 | // Fq2(u + 9)**(((q^11) - 1) / 6) 471 | Fq2 { 472 | c0: Fq([ 473 | 0xb5691c94bd4a6cd1, 474 | 0x56f575661b581478, 475 | 0x64708be5a7fb6f30, 476 | 0x2b462e5e77aecd82, 477 | ]), 478 | c1: Fq([ 479 | 0x2c63ef42612a1180, 480 | 0x29f16aae345bec69, 481 | 0xf95e18c648b216a4, 482 | 0x1aa36073a4cae0d4, 483 | ]), 484 | }, 485 | ]; 486 | 487 | #[cfg(test)] 488 | use rand::SeedableRng; 489 | #[cfg(test)] 490 | use rand_xorshift::XorShiftRng; 491 | 492 | #[test] 493 | fn test_fq12_mul_by_014() { 494 | let mut rng = XorShiftRng::from_seed([ 495 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 496 | 0xe5, 497 | ]); 498 | 499 | for _ in 0..1000 { 500 | let c0 = Fq2::random(&mut rng); 501 | let c1 = Fq2::random(&mut rng); 502 | let c5 = Fq2::random(&mut rng); 503 | let mut a = Fq12::random(&mut rng); 504 | let mut b = a; 505 | 506 | a.mul_by_014(&c0, &c1, &c5); 507 | b.mul_assign(&Fq12 { 508 | c0: Fq6 { 509 | c0, 510 | c1, 511 | c2: Fq2::zero(), 512 | }, 513 | c1: Fq6 { 514 | c0: Fq2::zero(), 515 | c1: c5, 516 | c2: Fq2::zero(), 517 | }, 518 | }); 519 | 520 | assert_eq!(a, b); 521 | } 522 | } 523 | 524 | #[test] 525 | fn test_fq12_mul_by_034() { 526 | let mut rng = XorShiftRng::from_seed([ 527 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 528 | 0xe5, 529 | ]); 530 | 531 | for _ in 0..1000 { 532 | let c0 = Fq2::random(&mut rng); 533 | let c3 = Fq2::random(&mut rng); 534 | let c4 = Fq2::random(&mut rng); 535 | let mut a = Fq12::random(&mut rng); 536 | let mut b = a; 537 | 538 | a.mul_by_034(&c0, &c3, &c4); 539 | b.mul_assign(&Fq12 { 540 | c0: Fq6 { 541 | c0, 542 | c1: Fq2::zero(), 543 | c2: Fq2::zero(), 544 | }, 545 | c1: Fq6 { 546 | c0: c3, 547 | c1: c4, 548 | c2: Fq2::zero(), 549 | }, 550 | }); 551 | 552 | assert_eq!(a, b); 553 | } 554 | } 555 | 556 | #[test] 557 | fn test_squaring() { 558 | let mut rng = XorShiftRng::from_seed([ 559 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 560 | 0xe5, 561 | ]); 562 | 563 | for _ in 0..1000 { 564 | let mut a = Fq12::random(&mut rng); 565 | let mut b = a; 566 | b.mul_assign(&a); 567 | a.square_assign(); 568 | assert_eq!(a, b); 569 | } 570 | } 571 | 572 | #[test] 573 | fn test_frobenius() { 574 | let mut rng = XorShiftRng::from_seed([ 575 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 576 | 0xe5, 577 | ]); 578 | 579 | for _ in 0..100 { 580 | for i in 0..14 { 581 | let mut a = Fq12::random(&mut rng); 582 | let mut b = a; 583 | 584 | for _ in 0..i { 585 | a = a.pow_vartime([ 586 | 0x3c208c16d87cfd47, 587 | 0x97816a916871ca8d, 588 | 0xb85045b68181585d, 589 | 0x30644e72e131a029, 590 | ]); 591 | } 592 | b.frobenius_map(i); 593 | 594 | assert_eq!(a, b); 595 | } 596 | } 597 | } 598 | 599 | #[test] 600 | fn test_field() { 601 | crate::tests::field::random_field_tests::("fq12".to_string()); 602 | } 603 | -------------------------------------------------------------------------------- /src/bn256/fr.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "asm")] 2 | use crate::bn256::assembly::field_arithmetic_asm; 3 | #[cfg(not(feature = "asm"))] 4 | use crate::{field_arithmetic, field_specific}; 5 | 6 | #[cfg(feature = "bn256-table")] 7 | #[rustfmt::skip] 8 | mod table; 9 | #[cfg(feature = "bn256-table")] 10 | #[cfg(test)] 11 | mod table_tests; 12 | 13 | #[cfg(feature = "bn256-table")] 14 | // This table should have being generated by `build.rs`; 15 | // and stored in `src/bn256/fr/table.rs`. 16 | pub use table::FR_TABLE; 17 | 18 | #[cfg(not(feature = "bn256-table"))] 19 | use crate::impl_from_u64; 20 | 21 | use crate::arithmetic::{adc, mac, sbb}; 22 | use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; 23 | use crate::{ 24 | field_bits, field_common, impl_add_binop_specify_output, impl_binops_additive, 25 | impl_binops_additive_specify_output, impl_binops_multiplicative, 26 | impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, impl_sum_prod, 27 | }; 28 | use core::convert::TryInto; 29 | use core::fmt; 30 | use core::ops::{Add, Mul, Neg, Sub}; 31 | use rand::RngCore; 32 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 33 | 34 | #[cfg(feature = "derive_serde")] 35 | use serde::{Deserialize, Serialize}; 36 | 37 | /// This represents an element of $\mathbb{F}_r$ where 38 | /// 39 | /// `r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001` 40 | /// 41 | /// is the scalar field of the BN254 curve. 42 | // The internal representation of this type is four 64-bit unsigned 43 | // integers in little-endian order. `Fr` values are always in 44 | // Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. 45 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 46 | #[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] 47 | pub struct Fr(pub(crate) [u64; 4]); 48 | 49 | /// Constant representing the modulus 50 | /// r = 0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001 51 | const MODULUS: Fr = Fr([ 52 | 0x43e1f593f0000001, 53 | 0x2833e84879b97091, 54 | 0xb85045b68181585d, 55 | 0x30644e72e131a029, 56 | ]); 57 | 58 | /// The modulus as u32 limbs. 59 | #[cfg(not(target_pointer_width = "64"))] 60 | const MODULUS_LIMBS_32: [u32; 8] = [ 61 | 0xf000_0001, 62 | 0x43e1_f593, 63 | 0x79b9_7091, 64 | 0x2833_e848, 65 | 0x8181_585d, 66 | 0xb850_45b6, 67 | 0xe131_a029, 68 | 0x3064_4e72, 69 | ]; 70 | 71 | const MODULUS_STR: &str = "0x30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001"; 72 | 73 | /// INV = -(r^{-1} mod 2^64) mod 2^64 74 | const INV: u64 = 0xc2e1f593efffffff; 75 | 76 | /// `R = 2^256 mod r` 77 | /// `0xe0a77c19a07df2f666ea36f7879462e36fc76959f60cd29ac96341c4ffffffb` 78 | const R: Fr = Fr([ 79 | 0xac96341c4ffffffb, 80 | 0x36fc76959f60cd29, 81 | 0x666ea36f7879462e, 82 | 0x0e0a77c19a07df2f, 83 | ]); 84 | 85 | /// `R^2 = 2^512 mod r` 86 | /// `0x216d0b17f4e44a58c49833d53bb808553fe3ab1e35c59e31bb8e645ae216da7` 87 | const R2: Fr = Fr([ 88 | 0x1bb8e645ae216da7, 89 | 0x53fe3ab1e35c59e3, 90 | 0x8c49833d53bb8085, 91 | 0x0216d0b17f4e44a5, 92 | ]); 93 | 94 | /// `R^3 = 2^768 mod r` 95 | /// `0xcf8594b7fcc657c893cc664a19fcfed2a489cbe1cfbb6b85e94d8e1b4bf0040` 96 | const R3: Fr = Fr([ 97 | 0x5e94d8e1b4bf0040, 98 | 0x2a489cbe1cfbb6b8, 99 | 0x893cc664a19fcfed, 100 | 0x0cf8594b7fcc657c, 101 | ]); 102 | 103 | /// `GENERATOR = 7 mod r` is a generator of the `r - 1` order multiplicative 104 | /// subgroup, or in other words a primitive root of the field. 105 | const GENERATOR: Fr = Fr::from_raw([0x07, 0x00, 0x00, 0x00]); 106 | 107 | const S: u32 = 28; 108 | 109 | /// GENERATOR^t where t * 2^s + 1 = r 110 | /// with t odd. In other words, this 111 | /// is a 2^s root of unity. 112 | /// `0x3ddb9f5166d18b798865ea93dd31f743215cf6dd39329c8d34f1ed960c37c9c` 113 | const ROOT_OF_UNITY: Fr = Fr::from_raw([ 114 | 0xd34f1ed960c37c9c, 115 | 0x3215cf6dd39329c8, 116 | 0x98865ea93dd31f74, 117 | 0x03ddb9f5166d18b7, 118 | ]); 119 | 120 | /// 1 / 2 mod r 121 | const TWO_INV: Fr = Fr::from_raw([ 122 | 0xa1f0fac9f8000001, 123 | 0x9419f4243cdcb848, 124 | 0xdc2822db40c0ac2e, 125 | 0x183227397098d014, 126 | ]); 127 | 128 | /// 1 / ROOT_OF_UNITY mod r 129 | const ROOT_OF_UNITY_INV: Fr = Fr::from_raw([ 130 | 0x0ed3e50a414e6dba, 131 | 0xb22625f59115aba7, 132 | 0x1bbe587180f34361, 133 | 0x048127174daabc26, 134 | ]); 135 | 136 | /// GENERATOR^{2^s} where t * 2^s + 1 = r with t odd. In other words, this is a t root of unity. 137 | /// 0x09226b6e22c6f0ca64ec26aad4c86e715b5f898e5e963f25870e56bbe533e9a2 138 | const DELTA: Fr = Fr::from_raw([ 139 | 0x870e56bbe533e9a2, 140 | 0x5b5f898e5e963f25, 141 | 0x64ec26aad4c86e71, 142 | 0x09226b6e22c6f0ca, 143 | ]); 144 | 145 | /// `ZETA^3 = 1 mod r` where `ZETA^2 != 1 mod r` 146 | const ZETA: Fr = Fr::from_raw([ 147 | 0x8b17ea66b99c90dd, 148 | 0x5bfc41088d8daaa7, 149 | 0xb3c4d79d41a91758, 150 | 0x00, 151 | ]); 152 | 153 | impl_binops_additive!(Fr, Fr); 154 | impl_binops_multiplicative!(Fr, Fr); 155 | field_common!( 156 | Fr, 157 | MODULUS, 158 | INV, 159 | MODULUS_STR, 160 | TWO_INV, 161 | ROOT_OF_UNITY_INV, 162 | DELTA, 163 | ZETA, 164 | R, 165 | R2, 166 | R3 167 | ); 168 | impl_sum_prod!(Fr); 169 | 170 | #[cfg(not(feature = "bn256-table"))] 171 | impl_from_u64!(Fr, R2); 172 | #[cfg(feature = "bn256-table")] 173 | // A field element is represented in the montgomery form -- this allows for cheap mul_mod operations. 174 | // The catch is, if we build an Fr element, regardless of its format, we need to perform one big integer multiplication: 175 | // 176 | // Fr([val, 0, 0, 0]) * R2 177 | // 178 | // When the "bn256-table" feature is enabled, we read the Fr element directly from the table. 179 | // This avoids a big integer multiplication. 180 | // 181 | // We use a table with 2^16 entries when the element is smaller than 2^16. 182 | impl From for Fr { 183 | fn from(val: u64) -> Fr { 184 | if val < 65536 { 185 | FR_TABLE[val as usize] 186 | } else { 187 | Fr([val, 0, 0, 0]) * R2 188 | } 189 | } 190 | } 191 | 192 | #[cfg(not(feature = "asm"))] 193 | field_arithmetic!(Fr, MODULUS, INV, sparse); 194 | #[cfg(feature = "asm")] 195 | field_arithmetic_asm!(Fr, MODULUS, INV); 196 | 197 | #[cfg(target_pointer_width = "64")] 198 | field_bits!(Fr, MODULUS); 199 | #[cfg(not(target_pointer_width = "64"))] 200 | field_bits!(Fr, MODULUS, MODULUS_LIMBS_32); 201 | 202 | impl Fr { 203 | pub const fn size() -> usize { 204 | 32 205 | } 206 | } 207 | 208 | impl ff::Field for Fr { 209 | const ZERO: Self = Self::zero(); 210 | const ONE: Self = Self::one(); 211 | 212 | fn random(mut rng: impl RngCore) -> Self { 213 | Self::from_u512([ 214 | rng.next_u64(), 215 | rng.next_u64(), 216 | rng.next_u64(), 217 | rng.next_u64(), 218 | rng.next_u64(), 219 | rng.next_u64(), 220 | rng.next_u64(), 221 | rng.next_u64(), 222 | ]) 223 | } 224 | 225 | fn double(&self) -> Self { 226 | self.double() 227 | } 228 | 229 | #[inline(always)] 230 | fn square(&self) -> Self { 231 | self.square() 232 | } 233 | 234 | /// Returns the multiplicative inverse of the 235 | /// element. If it is zero, the method fails. 236 | fn invert(&self) -> CtOption { 237 | self.invert() 238 | } 239 | 240 | fn sqrt(&self) -> CtOption { 241 | /// `(t - 1) // 2` where t * 2^s + 1 = p with t odd. 242 | const T_MINUS1_OVER2: [u64; 4] = [ 243 | 0xcdcb848a1f0fac9f, 244 | 0x0c0ac2e9419f4243, 245 | 0x098d014dc2822db4, 246 | 0x0000000183227397, 247 | ]; 248 | ff::helpers::sqrt_tonelli_shanks(self, T_MINUS1_OVER2) 249 | } 250 | 251 | fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { 252 | ff::helpers::sqrt_ratio_generic(num, div) 253 | } 254 | } 255 | 256 | impl ff::PrimeField for Fr { 257 | type Repr = [u8; 32]; 258 | 259 | const NUM_BITS: u32 = 254; 260 | const CAPACITY: u32 = 253; 261 | const MODULUS: &'static str = MODULUS_STR; 262 | const MULTIPLICATIVE_GENERATOR: Self = GENERATOR; 263 | const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; 264 | const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; 265 | const TWO_INV: Self = TWO_INV; 266 | const DELTA: Self = DELTA; 267 | const S: u32 = S; 268 | 269 | fn from_repr(repr: Self::Repr) -> CtOption { 270 | let mut tmp = Fr([0, 0, 0, 0]); 271 | 272 | tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); 273 | tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); 274 | tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); 275 | tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); 276 | 277 | // Try to subtract the modulus 278 | let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); 279 | let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); 280 | let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); 281 | let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); 282 | 283 | // If the element is smaller than MODULUS then the 284 | // subtraction will underflow, producing a borrow value 285 | // of 0xffff...ffff. Otherwise, it'll be zero. 286 | let is_some = (borrow as u8) & 1; 287 | 288 | // Convert to Montgomery form by computing 289 | // (a.R^0 * R^2) / R = a.R 290 | tmp *= &R2; 291 | 292 | CtOption::new(tmp, Choice::from(is_some)) 293 | } 294 | 295 | fn to_repr(&self) -> Self::Repr { 296 | // Turn into canonical form by computing 297 | // (a.R) / R = a 298 | let tmp = Fr::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); 299 | 300 | let mut res = [0; 32]; 301 | res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); 302 | res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); 303 | res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); 304 | res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); 305 | 306 | res 307 | } 308 | 309 | fn is_odd(&self) -> Choice { 310 | Choice::from(self.to_repr()[0] & 1) 311 | } 312 | } 313 | 314 | impl FromUniformBytes<64> for Fr { 315 | /// Converts a 512-bit little endian integer into 316 | /// an `Fr` by reducing by the modulus. 317 | fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { 318 | Self::from_u512([ 319 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 320 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 321 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 322 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 323 | u64::from_le_bytes(bytes[32..40].try_into().unwrap()), 324 | u64::from_le_bytes(bytes[40..48].try_into().unwrap()), 325 | u64::from_le_bytes(bytes[48..56].try_into().unwrap()), 326 | u64::from_le_bytes(bytes[56..64].try_into().unwrap()), 327 | ]) 328 | } 329 | } 330 | 331 | impl WithSmallOrderMulGroup<3> for Fr { 332 | const ZETA: Self = ZETA; 333 | } 334 | 335 | #[cfg(test)] 336 | mod test { 337 | use crate::serde::SerdeObject; 338 | 339 | use super::*; 340 | use ark_std::{end_timer, start_timer}; 341 | use ff::Field; 342 | use rand::SeedableRng; 343 | use rand_core::OsRng; 344 | use rand_xorshift::XorShiftRng; 345 | 346 | #[test] 347 | fn test_sqrt() { 348 | let v = (Fr::TWO_INV).square().sqrt().unwrap(); 349 | assert!(v == Fr::TWO_INV || (-v) == Fr::TWO_INV); 350 | 351 | for _ in 0..10000 { 352 | let a = Fr::random(OsRng); 353 | let mut b = a; 354 | b = b.square(); 355 | 356 | let b = b.sqrt().unwrap(); 357 | let mut negb = b; 358 | negb = negb.neg(); 359 | 360 | assert!(a == b || a == negb); 361 | } 362 | } 363 | 364 | #[test] 365 | fn test_field() { 366 | crate::tests::field::random_field_tests::("bn256 scalar".to_string()); 367 | } 368 | 369 | #[test] 370 | fn test_delta() { 371 | assert_eq!(Fr::DELTA, GENERATOR.pow([1u64 << Fr::S])); 372 | assert_eq!(Fr::DELTA, Fr::MULTIPLICATIVE_GENERATOR.pow([1u64 << Fr::S])); 373 | } 374 | 375 | #[test] 376 | fn test_from_u512() { 377 | assert_eq!( 378 | Fr::from_raw([ 379 | 0x7e7140b5196b9e6f, 380 | 0x9abac9e4157b6172, 381 | 0xf04bc41062fd7322, 382 | 0x1185fa9c9fef6326, 383 | ]), 384 | Fr::from_u512([ 385 | 0xaaaaaaaaaaaaaaaa, 386 | 0xaaaaaaaaaaaaaaaa, 387 | 0xaaaaaaaaaaaaaaaa, 388 | 0xaaaaaaaaaaaaaaaa, 389 | 0xaaaaaaaaaaaaaaaa, 390 | 0xaaaaaaaaaaaaaaaa, 391 | 0xaaaaaaaaaaaaaaaa, 392 | 0xaaaaaaaaaaaaaaaa 393 | ]) 394 | ); 395 | } 396 | 397 | #[test] 398 | #[cfg(feature = "bits")] 399 | fn test_bits() { 400 | crate::tests::field::random_bits_tests::("fr".to_string()); 401 | } 402 | 403 | #[test] 404 | fn test_serialization() { 405 | crate::tests::field::random_serialization_test::("fr".to_string()); 406 | #[cfg(feature = "derive_serde")] 407 | crate::tests::field::random_serde_test::("fr".to_string()); 408 | } 409 | 410 | fn is_less_than(x: &[u64; 4], y: &[u64; 4]) -> bool { 411 | match x[3].cmp(&y[3]) { 412 | core::cmp::Ordering::Less => return true, 413 | core::cmp::Ordering::Greater => return false, 414 | _ => {} 415 | } 416 | match x[2].cmp(&y[2]) { 417 | core::cmp::Ordering::Less => return true, 418 | core::cmp::Ordering::Greater => return false, 419 | _ => {} 420 | } 421 | match x[1].cmp(&y[1]) { 422 | core::cmp::Ordering::Less => return true, 423 | core::cmp::Ordering::Greater => return false, 424 | _ => {} 425 | } 426 | x[0].lt(&y[0]) 427 | } 428 | 429 | #[test] 430 | fn test_serialization_check() { 431 | let mut rng = XorShiftRng::from_seed([ 432 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 433 | 0xbc, 0xe5, 434 | ]); 435 | let start = start_timer!(|| "serialize fr"); 436 | // failure check 437 | for _ in 0..1000000 { 438 | let rand_word = [(); 4].map(|_| rng.next_u64()); 439 | let a = Fr(rand_word); 440 | let rand_bytes = a.to_raw_bytes(); 441 | match is_less_than(&rand_word, &MODULUS.0) { 442 | false => { 443 | assert!(Fr::from_raw_bytes(&rand_bytes).is_none()); 444 | } 445 | _ => { 446 | assert_eq!(Fr::from_raw_bytes(&rand_bytes), Some(a)); 447 | } 448 | } 449 | } 450 | end_timer!(start); 451 | } 452 | 453 | #[test] 454 | fn bench_fr_from_u16() { 455 | let repeat = 10000000; 456 | let mut rng = ark_std::test_rng(); 457 | let base = (0..repeat).map(|_| (rng.next_u32() % (1 << 16)) as u64); 458 | 459 | let timer = start_timer!(|| format!("generate {repeat} Bn256 scalar field elements")); 460 | let _res: Vec<_> = base.map(Fr::from).collect(); 461 | 462 | end_timer!(timer); 463 | } 464 | } 465 | -------------------------------------------------------------------------------- /src/bn256/fr/table_tests.rs: -------------------------------------------------------------------------------- 1 | use crate::bn256::{Fr, FR_TABLE}; 2 | 3 | #[test] 4 | fn test_table() { 5 | for (i, e) in FR_TABLE.iter().enumerate() { 6 | assert_eq!(Fr::from(i as u64), *e); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/bn256/mod.rs: -------------------------------------------------------------------------------- 1 | mod curve; 2 | mod engine; 3 | mod fq; 4 | mod fq12; 5 | mod fq2; 6 | mod fq6; 7 | mod fr; 8 | 9 | #[cfg(feature = "asm")] 10 | mod assembly; 11 | 12 | pub use curve::*; 13 | pub use engine::*; 14 | pub use fq::*; 15 | pub use fq12::*; 16 | pub use fq2::*; 17 | pub use fq6::*; 18 | pub use fr::*; 19 | 20 | #[derive(Debug, PartialEq, Eq)] 21 | pub enum LegendreSymbol { 22 | Zero = 0, 23 | QuadraticResidue = 1, 24 | QuadraticNonResidue = -1, 25 | } 26 | -------------------------------------------------------------------------------- /src/derive/mod.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | pub mod curve; 3 | #[macro_use] 4 | pub mod field; 5 | 6 | #[macro_export] 7 | macro_rules! impl_add_binop_specify_output { 8 | ($lhs:ident, $rhs:ident, $output:ident) => { 9 | impl<'b> ::core::ops::Add<&'b $rhs> for $lhs { 10 | type Output = $output; 11 | 12 | #[inline] 13 | fn add(self, rhs: &'b $rhs) -> $output { 14 | &self + rhs 15 | } 16 | } 17 | 18 | impl<'a> ::core::ops::Add<$rhs> for &'a $lhs { 19 | type Output = $output; 20 | 21 | #[inline] 22 | fn add(self, rhs: $rhs) -> $output { 23 | self + &rhs 24 | } 25 | } 26 | 27 | impl ::core::ops::Add<$rhs> for $lhs { 28 | type Output = $output; 29 | 30 | #[inline] 31 | fn add(self, rhs: $rhs) -> $output { 32 | &self + &rhs 33 | } 34 | } 35 | }; 36 | } 37 | 38 | #[macro_export] 39 | macro_rules! impl_sub_binop_specify_output { 40 | ($lhs:ident, $rhs:ident, $output:ident) => { 41 | impl<'b> ::core::ops::Sub<&'b $rhs> for $lhs { 42 | type Output = $output; 43 | 44 | #[inline] 45 | fn sub(self, rhs: &'b $rhs) -> $output { 46 | &self - rhs 47 | } 48 | } 49 | 50 | impl<'a> ::core::ops::Sub<$rhs> for &'a $lhs { 51 | type Output = $output; 52 | 53 | #[inline] 54 | fn sub(self, rhs: $rhs) -> $output { 55 | self - &rhs 56 | } 57 | } 58 | 59 | impl ::core::ops::Sub<$rhs> for $lhs { 60 | type Output = $output; 61 | 62 | #[inline] 63 | fn sub(self, rhs: $rhs) -> $output { 64 | &self - &rhs 65 | } 66 | } 67 | }; 68 | } 69 | 70 | #[macro_export] 71 | macro_rules! impl_binops_additive_specify_output { 72 | ($lhs:ident, $rhs:ident, $output:ident) => { 73 | impl_add_binop_specify_output!($lhs, $rhs, $output); 74 | impl_sub_binop_specify_output!($lhs, $rhs, $output); 75 | }; 76 | } 77 | 78 | #[macro_export] 79 | macro_rules! impl_binops_multiplicative_mixed { 80 | ($lhs:ident, $rhs:ident, $output:ident) => { 81 | impl<'b> ::core::ops::Mul<&'b $rhs> for $lhs { 82 | type Output = $output; 83 | 84 | #[inline] 85 | fn mul(self, rhs: &'b $rhs) -> $output { 86 | &self * rhs 87 | } 88 | } 89 | 90 | impl<'a> ::core::ops::Mul<$rhs> for &'a $lhs { 91 | type Output = $output; 92 | 93 | #[inline] 94 | fn mul(self, rhs: $rhs) -> $output { 95 | self * &rhs 96 | } 97 | } 98 | 99 | impl ::core::ops::Mul<$rhs> for $lhs { 100 | type Output = $output; 101 | 102 | #[inline] 103 | fn mul(self, rhs: $rhs) -> $output { 104 | &self * &rhs 105 | } 106 | } 107 | }; 108 | } 109 | 110 | #[macro_export] 111 | macro_rules! impl_binops_additive { 112 | ($lhs:ident, $rhs:ident) => { 113 | impl_binops_additive_specify_output!($lhs, $rhs, $lhs); 114 | 115 | impl ::core::ops::SubAssign<$rhs> for $lhs { 116 | #[inline] 117 | fn sub_assign(&mut self, rhs: $rhs) { 118 | *self = &*self - &rhs; 119 | } 120 | } 121 | 122 | impl ::core::ops::AddAssign<$rhs> for $lhs { 123 | #[inline] 124 | fn add_assign(&mut self, rhs: $rhs) { 125 | *self = &*self + &rhs; 126 | } 127 | } 128 | 129 | impl<'b> ::core::ops::SubAssign<&'b $rhs> for $lhs { 130 | #[inline] 131 | fn sub_assign(&mut self, rhs: &'b $rhs) { 132 | *self = &*self - rhs; 133 | } 134 | } 135 | 136 | impl<'b> ::core::ops::AddAssign<&'b $rhs> for $lhs { 137 | #[inline] 138 | fn add_assign(&mut self, rhs: &'b $rhs) { 139 | *self = &*self + rhs; 140 | } 141 | } 142 | }; 143 | } 144 | 145 | #[macro_export] 146 | macro_rules! impl_binops_multiplicative { 147 | ($lhs:ident, $rhs:ident) => { 148 | impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); 149 | 150 | impl ::core::ops::MulAssign<$rhs> for $lhs { 151 | #[inline] 152 | fn mul_assign(&mut self, rhs: $rhs) { 153 | *self = &*self * &rhs; 154 | } 155 | } 156 | 157 | impl<'b> ::core::ops::MulAssign<&'b $rhs> for $lhs { 158 | #[inline] 159 | fn mul_assign(&mut self, rhs: &'b $rhs) { 160 | *self = &*self * rhs; 161 | } 162 | } 163 | }; 164 | } 165 | 166 | #[macro_export] 167 | macro_rules! impl_sum_prod { 168 | ($f:ident) => { 169 | impl> ::core::iter::Sum for $f { 170 | fn sum>(iter: I) -> Self { 171 | use ::ff::Field; 172 | iter.fold(Self::ZERO, |acc, item| acc + item.borrow()) 173 | } 174 | } 175 | 176 | impl> ::core::iter::Product for $f { 177 | fn product>(iter: I) -> Self { 178 | use ::ff::Field; 179 | iter.fold(Self::ONE, |acc, item| acc * item.borrow()) 180 | } 181 | } 182 | }; 183 | } 184 | -------------------------------------------------------------------------------- /src/ff_inverse.rs: -------------------------------------------------------------------------------- 1 | use core::cmp::PartialEq; 2 | use std::ops::{Add, Mul, Neg, Sub}; 3 | 4 | /// Big signed (B * L)-bit integer type, whose variables store 5 | /// numbers in the two's complement code as arrays of B-bit chunks. 6 | /// The ordering of the chunks in these arrays is little-endian. 7 | /// The arithmetic operations for this type are wrapping ones. 8 | #[derive(Clone)] 9 | struct CInt(pub [u64; L]); 10 | 11 | impl CInt { 12 | /// Mask, in which the B lowest bits are 1 and only they 13 | pub const MASK: u64 = u64::MAX >> (64 - B); 14 | 15 | /// Representation of -1 16 | pub const MINUS_ONE: Self = Self([Self::MASK; L]); 17 | 18 | /// Representation of 0 19 | pub const ZERO: Self = Self([0; L]); 20 | 21 | /// Representation of 1 22 | pub const ONE: Self = { 23 | let mut data = [0; L]; 24 | data[0] = 1; 25 | Self(data) 26 | }; 27 | 28 | /// Returns the result of applying B-bit right 29 | /// arithmetical shift to the current number 30 | pub fn shift(&self) -> Self { 31 | let mut data = [0; L]; 32 | if self.is_negative() { 33 | data[L - 1] = Self::MASK; 34 | } 35 | data[..L - 1].copy_from_slice(&self.0[1..]); 36 | Self(data) 37 | } 38 | 39 | /// Returns the lowest B bits of the current number 40 | pub fn lowest(&self) -> u64 { 41 | self.0[0] 42 | } 43 | 44 | /// Returns "true" iff the current number is negative 45 | pub fn is_negative(&self) -> bool { 46 | self.0[L - 1] > (Self::MASK >> 1) 47 | } 48 | } 49 | 50 | impl PartialEq for CInt { 51 | fn eq(&self, other: &Self) -> bool { 52 | self.0 == other.0 53 | } 54 | } 55 | 56 | impl Add for &CInt { 57 | type Output = CInt; 58 | fn add(self, other: Self) -> Self::Output { 59 | let (mut data, mut carry) = ([0; L], 0); 60 | for i in 0..L { 61 | let sum = self.0[i] + other.0[i] + carry; 62 | data[i] = sum & CInt::::MASK; 63 | carry = sum >> B; 64 | } 65 | Self::Output { 0: data } 66 | } 67 | } 68 | 69 | impl Add<&CInt> for CInt { 70 | type Output = CInt; 71 | fn add(self, other: &Self) -> Self::Output { 72 | &self + other 73 | } 74 | } 75 | 76 | impl Add for CInt { 77 | type Output = CInt; 78 | fn add(self, other: Self) -> Self::Output { 79 | &self + &other 80 | } 81 | } 82 | 83 | impl Sub for &CInt { 84 | type Output = CInt; 85 | fn sub(self, other: Self) -> Self::Output { 86 | // For the two's complement code the additive negation is the result of 87 | // adding 1 to the bitwise inverted argument's representation. Thus, for 88 | // any encoded integers x and y we have x - y = x + !y + 1, where "!" is 89 | // the bitwise inversion and addition is done according to the rules of 90 | // the code. The algorithm below uses this formula and is the modified 91 | // addition algorithm, where the carry flag is initialized with 1 and 92 | // the chunks of the second argument are bitwise inverted 93 | let (mut data, mut carry) = ([0; L], 1); 94 | for i in 0..L { 95 | let sum = self.0[i] + (other.0[i] ^ CInt::::MASK) + carry; 96 | data[i] = sum & CInt::::MASK; 97 | carry = sum >> B; 98 | } 99 | Self::Output { 0: data } 100 | } 101 | } 102 | 103 | impl Sub<&CInt> for CInt { 104 | type Output = CInt; 105 | fn sub(self, other: &Self) -> Self::Output { 106 | &self - other 107 | } 108 | } 109 | 110 | impl Sub for CInt { 111 | type Output = CInt; 112 | fn sub(self, other: Self) -> Self::Output { 113 | &self - &other 114 | } 115 | } 116 | 117 | impl Neg for &CInt { 118 | type Output = CInt; 119 | fn neg(self) -> Self::Output { 120 | // For the two's complement code the additive negation is the result 121 | // of adding 1 to the bitwise inverted argument's representation 122 | let (mut data, mut carry) = ([0; L], 1); 123 | for i in 0..L { 124 | let sum = (self.0[i] ^ CInt::::MASK) + carry; 125 | data[i] = sum & CInt::::MASK; 126 | carry = sum >> B; 127 | } 128 | Self::Output { 0: data } 129 | } 130 | } 131 | 132 | impl Neg for CInt { 133 | type Output = CInt; 134 | fn neg(self) -> Self::Output { 135 | -&self 136 | } 137 | } 138 | 139 | impl Mul for &CInt { 140 | type Output = CInt; 141 | fn mul(self, other: Self) -> Self::Output { 142 | let mut data = [0; L]; 143 | for i in 0..L { 144 | let mut carry = 0; 145 | for k in 0..(L - i) { 146 | let sum = (data[i + k] as u128) 147 | + (carry as u128) 148 | + (self.0[i] as u128) * (other.0[k] as u128); 149 | data[i + k] = sum as u64 & CInt::::MASK; 150 | carry = (sum >> B) as u64; 151 | } 152 | } 153 | Self::Output { 0: data } 154 | } 155 | } 156 | 157 | impl Mul<&CInt> for CInt { 158 | type Output = CInt; 159 | fn mul(self, other: &Self) -> Self::Output { 160 | &self * other 161 | } 162 | } 163 | 164 | impl Mul for CInt { 165 | type Output = CInt; 166 | fn mul(self, other: Self) -> Self::Output { 167 | &self * &other 168 | } 169 | } 170 | 171 | impl Mul for &CInt { 172 | type Output = CInt; 173 | fn mul(self, other: i64) -> Self::Output { 174 | let mut data = [0; L]; 175 | // If the short multiplicand is non-negative, the standard multiplication 176 | // algorithm is performed. Otherwise, the product of the additively negated 177 | // multiplicands is found as follows. Since for the two's complement code 178 | // the additive negation is the result of adding 1 to the bitwise inverted 179 | // argument's representation, for any encoded integers x and y we have 180 | // x * y = (-x) * (-y) = (!x + 1) * (-y) = !x * (-y) + (-y), where "!" is 181 | // the bitwise inversion and arithmetic operations are performed according 182 | // to the rules of the code. If the short multiplicand is negative, the 183 | // algorithm below uses this formula by substituting the short multiplicand 184 | // for y and turns into the modified standard multiplication algorithm, 185 | // where the carry flag is initialized with the additively negated short 186 | // multiplicand and the chunks of the long multiplicand are bitwise inverted 187 | let (other, mut carry, mask) = if other < 0 { 188 | (-other, -other as u64, CInt::::MASK) 189 | } else { 190 | (other, 0, 0) 191 | }; 192 | for i in 0..L { 193 | let sum = (carry as u128) + ((self.0[i] ^ mask) as u128) * (other as u128); 194 | data[i] = sum as u64 & CInt::::MASK; 195 | carry = (sum >> B) as u64; 196 | } 197 | Self::Output { 0: data } 198 | } 199 | } 200 | 201 | impl Mul for CInt { 202 | type Output = CInt; 203 | fn mul(self, other: i64) -> Self::Output { 204 | &self * other 205 | } 206 | } 207 | 208 | impl Mul<&CInt> for i64 { 209 | type Output = CInt; 210 | fn mul(self, other: &CInt) -> Self::Output { 211 | other * self 212 | } 213 | } 214 | 215 | impl Mul> for i64 { 216 | type Output = CInt; 217 | fn mul(self, other: CInt) -> Self::Output { 218 | other * self 219 | } 220 | } 221 | 222 | /// Type of the modular multiplicative inverter based on the Bernstein-Yang method. 223 | /// The inverter can be created for a specified modulus M and adjusting parameter A 224 | /// to compute the adjusted multiplicative inverses of positive integers, i.e. for 225 | /// computing (1 / x) * A (mod M) for a positive integer x. 226 | /// 227 | /// The adjusting parameter allows computing the multiplicative inverses in the case of 228 | /// using the Montgomery representation for the input or the expected output. If R is 229 | /// the Montgomery factor, the multiplicative inverses in the appropriate representation 230 | /// can be computed provided that the value of A is chosen as follows: 231 | /// - A = 1, if both the input and the expected output are in the standard form 232 | /// - A = R^2 mod M, if both the input and the expected output are in the Montgomery form 233 | /// - A = R mod M, if either the input or the expected output is in the Montgomery form, 234 | /// but not both of them 235 | /// 236 | /// The public methods of this type receive and return unsigned big integers as arrays of 237 | /// 64-bit chunks, the ordering of which is little-endian. Both the modulus and the integer 238 | /// to be inverted should not exceed 2 ^ (62 * L - 64) 239 | /// 240 | /// For better understanding the implementation, the following resources are recommended: 241 | /// - D. Bernstein, B.-Y. Yang, "Fast constant-time gcd computation and modular inversion", 242 | /// https://gcd.cr.yp.to/safegcd-20190413.pdf 243 | /// - P. Wuille, "The safegcd implementation in libsecp256k1 explained", 244 | /// https://github.com/bitcoin-core/secp256k1/blob/master/doc/safegcd_implementation.md 245 | pub struct BYInverter { 246 | /// Modulus 247 | modulus: CInt<62, L>, 248 | 249 | /// Adjusting parameter 250 | adjuster: CInt<62, L>, 251 | 252 | /// Multiplicative inverse of the modulus modulo 2^62 253 | inverse: i64, 254 | } 255 | 256 | /// Type of the Bernstein-Yang transition matrix multiplied by 2^62 257 | type Matrix = [[i64; 2]; 2]; 258 | 259 | impl BYInverter { 260 | /// Returns the Bernstein-Yang transition matrix multiplied by 2^62 and the new value 261 | /// of the delta variable for the 62 basic steps of the Bernstein-Yang method, which 262 | /// are to be performed sequentially for specified initial values of f, g and delta 263 | fn jump(f: &CInt<62, L>, g: &CInt<62, L>, mut delta: i64) -> (i64, Matrix) { 264 | let (mut steps, mut f, mut g) = (62, f.lowest() as i64, g.lowest() as i128); 265 | let mut t: Matrix = [[1, 0], [0, 1]]; 266 | 267 | loop { 268 | let zeros = steps.min(g.trailing_zeros() as i64); 269 | (steps, delta, g) = (steps - zeros, delta + zeros, g >> zeros); 270 | t[0] = [t[0][0] << zeros, t[0][1] << zeros]; 271 | 272 | if steps == 0 { 273 | break; 274 | } 275 | if delta > 0 { 276 | (delta, f, g) = (-delta, g as i64, -f as i128); 277 | (t[0], t[1]) = (t[1], [-t[0][0], -t[0][1]]); 278 | } 279 | 280 | // The formula (3 * x) xor 28 = -1 / x (mod 32) for an odd integer x 281 | // in the two's complement code has been derived from the formula 282 | // (3 * x) xor 2 = 1 / x (mod 32) attributed to Peter Montgomery 283 | let mask = (1 << steps.min(1 - delta).min(5)) - 1; 284 | let w = (g as i64).wrapping_mul(f.wrapping_mul(3) ^ 28) & mask; 285 | 286 | t[1] = [t[0][0] * w + t[1][0], t[0][1] * w + t[1][1]]; 287 | g += w as i128 * f as i128; 288 | } 289 | 290 | (delta, t) 291 | } 292 | 293 | /// Returns the updated values of the variables f and g for specified initial ones and Bernstein-Yang transition 294 | /// matrix multiplied by 2^62. The returned vector is "matrix * (f, g)' / 2^62", where "'" is the transpose operator 295 | fn fg(f: CInt<62, L>, g: CInt<62, L>, t: Matrix) -> (CInt<62, L>, CInt<62, L>) { 296 | ( 297 | (t[0][0] * &f + t[0][1] * &g).shift(), 298 | (t[1][0] * &f + t[1][1] * &g).shift(), 299 | ) 300 | } 301 | 302 | /// Returns the updated values of the variables d and e for specified initial ones and Bernstein-Yang transition 303 | /// matrix multiplied by 2^62. The returned vector is congruent modulo M to "matrix * (d, e)' / 2^62 (mod M)", 304 | /// where M is the modulus the inverter was created for and "'" stands for the transpose operator. Both the input 305 | /// and output values lie in the interval (-2 * M, M) 306 | fn de(&self, d: CInt<62, L>, e: CInt<62, L>, t: Matrix) -> (CInt<62, L>, CInt<62, L>) { 307 | let mask = CInt::<62, L>::MASK as i64; 308 | let mut md = t[0][0] * d.is_negative() as i64 + t[0][1] * e.is_negative() as i64; 309 | let mut me = t[1][0] * d.is_negative() as i64 + t[1][1] * e.is_negative() as i64; 310 | 311 | let cd = t[0][0] 312 | .wrapping_mul(d.lowest() as i64) 313 | .wrapping_add(t[0][1].wrapping_mul(e.lowest() as i64)) 314 | & mask; 315 | let ce = t[1][0] 316 | .wrapping_mul(d.lowest() as i64) 317 | .wrapping_add(t[1][1].wrapping_mul(e.lowest() as i64)) 318 | & mask; 319 | 320 | md -= (self.inverse.wrapping_mul(cd).wrapping_add(md)) & mask; 321 | me -= (self.inverse.wrapping_mul(ce).wrapping_add(me)) & mask; 322 | 323 | let cd = t[0][0] * &d + t[0][1] * &e + md * &self.modulus; 324 | let ce = t[1][0] * &d + t[1][1] * &e + me * &self.modulus; 325 | 326 | (cd.shift(), ce.shift()) 327 | } 328 | 329 | /// Returns either "value (mod M)" or "-value (mod M)", where M is the modulus the 330 | /// inverter was created for, depending on "negate", which determines the presence 331 | /// of "-" in the used formula. The input integer lies in the interval (-2 * M, M) 332 | fn norm(&self, mut value: CInt<62, L>, negate: bool) -> CInt<62, L> { 333 | if value.is_negative() { 334 | value = value + &self.modulus; 335 | } 336 | 337 | if negate { 338 | value = -value; 339 | } 340 | 341 | if value.is_negative() { 342 | value = value + &self.modulus; 343 | } 344 | 345 | value 346 | } 347 | 348 | /// Returns a big unsigned integer as an array of O-bit chunks, which is equal modulo 349 | /// 2 ^ (O * S) to the input big unsigned integer stored as an array of I-bit chunks. 350 | /// The ordering of the chunks in these arrays is little-endian 351 | const fn convert(input: &[u64]) -> [u64; S] { 352 | // This function is defined because the method "min" of the usize type is not constant 353 | const fn min(a: usize, b: usize) -> usize { 354 | if a > b { 355 | b 356 | } else { 357 | a 358 | } 359 | } 360 | 361 | let (total, mut output, mut bits) = (min(input.len() * I, S * O), [0; S], 0); 362 | 363 | while bits < total { 364 | let (i, o) = (bits % I, bits % O); 365 | output[bits / O] |= (input[bits / I] >> i) << o; 366 | bits += min(I - i, O - o); 367 | } 368 | 369 | let mask = u64::MAX >> (64 - O); 370 | let mut filled = total / O + if total % O > 0 { 1 } else { 0 }; 371 | 372 | while filled > 0 { 373 | filled -= 1; 374 | output[filled] &= mask; 375 | } 376 | 377 | output 378 | } 379 | 380 | /// Returns the multiplicative inverse of the argument modulo 2^62. The implementation is based 381 | /// on the Hurchalla's method for computing the multiplicative inverse modulo a power of two. 382 | /// For better understanding the implementation, the following paper is recommended: 383 | /// J. Hurchalla, "An Improved Integer Multiplicative Inverse (modulo 2^w)", 384 | /// https://arxiv.org/pdf/2204.04342.pdf 385 | const fn inv(value: u64) -> i64 { 386 | let x = value.wrapping_mul(3) ^ 2; 387 | let y = 1u64.wrapping_sub(x.wrapping_mul(value)); 388 | let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y)); 389 | let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y)); 390 | let (x, y) = (x.wrapping_mul(y.wrapping_add(1)), y.wrapping_mul(y)); 391 | (x.wrapping_mul(y.wrapping_add(1)) & CInt::<62, L>::MASK) as i64 392 | } 393 | 394 | /// Creates the inverter for specified modulus and adjusting parameter 395 | pub const fn new(modulus: &[u64], adjuster: &[u64]) -> Self { 396 | Self { 397 | modulus: CInt::<62, L>(Self::convert::<64, 62, L>(modulus)), 398 | adjuster: CInt::<62, L>(Self::convert::<64, 62, L>(adjuster)), 399 | inverse: Self::inv(modulus[0]), 400 | } 401 | } 402 | 403 | /// Returns either the adjusted modular multiplicative inverse for the argument or None 404 | /// depending on invertibility of the argument, i.e. its coprimality with the modulus 405 | pub fn invert(&self, value: &[u64]) -> Option<[u64; S]> { 406 | let (mut d, mut e) = (CInt::ZERO, self.adjuster.clone()); 407 | let mut g = CInt::<62, L>(Self::convert::<64, 62, L>(value)); 408 | let (mut delta, mut f) = (1, self.modulus.clone()); 409 | let mut matrix; 410 | while g != CInt::ZERO { 411 | (delta, matrix) = Self::jump(&f, &g, delta); 412 | (f, g) = Self::fg(f, g, matrix); 413 | (d, e) = self.de(d, e, matrix); 414 | } 415 | // At this point the absolute value of "f" equals the greatest common divisor 416 | // of the integer to be inverted and the modulus the inverter was created for. 417 | // Thus, if "f" is neither 1 nor -1, then the sought inverse does not exist 418 | let antiunit = f == CInt::MINUS_ONE; 419 | if (f != CInt::ONE) && !antiunit { 420 | return None; 421 | } 422 | Some(Self::convert::<62, 64, S>(&self.norm(d, antiunit).0)) 423 | } 424 | } 425 | -------------------------------------------------------------------------------- /src/grumpkin/curve.rs: -------------------------------------------------------------------------------- 1 | use crate::ff::WithSmallOrderMulGroup; 2 | use crate::ff::{Field, PrimeField}; 3 | use crate::group::Curve; 4 | use crate::group::{prime::PrimeCurveAffine, Group, GroupEncoding}; 5 | use crate::grumpkin::Fq; 6 | use crate::grumpkin::Fr; 7 | use crate::hash_to_curve::svdw_hash_to_curve; 8 | use crate::{ 9 | batch_add, impl_add_binop_specify_output, impl_binops_additive, 10 | impl_binops_additive_specify_output, impl_binops_multiplicative, 11 | impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, new_curve_impl, 12 | }; 13 | use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt}; 14 | use core::cmp; 15 | use core::fmt::Debug; 16 | use core::iter::Sum; 17 | use core::ops::{Add, Mul, Neg, Sub}; 18 | use rand::RngCore; 19 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 20 | 21 | #[cfg(feature = "derive_serde")] 22 | use serde::{Deserialize, Serialize}; 23 | 24 | new_curve_impl!( 25 | (pub), 26 | G1, 27 | G1Affine, 28 | false, 29 | Fq, 30 | Fr, 31 | (G1_GENERATOR_X, G1_GENERATOR_Y), 32 | G1_A, 33 | G1_B, 34 | "grumpkin_g1", 35 | |curve_id, domain_prefix| svdw_hash_to_curve(curve_id, domain_prefix, G1::SVDW_Z), 36 | ); 37 | 38 | impl CurveAffineExt for G1Affine { 39 | batch_add!(); 40 | 41 | fn into_coordinates(self) -> (Self::Base, Self::Base) { 42 | (self.x, self.y) 43 | } 44 | } 45 | 46 | // Parameters in montgomery form taken from 47 | // https://github.com/AztecProtocol/barretenberg/blob/97ccf76c42db581a8b8f8bfbcffe8ca015a3dd22/cpp/src/barretenberg/ecc/curves/grumpkin/grumpkin.hpp#L14 48 | const G1_GENERATOR_X: Fq = Fq::one(); 49 | const G1_GENERATOR_Y: Fq = Fq([ 50 | 0x11b2dff1448c41d8, 51 | 0x23d3446f21c77dc3, 52 | 0xaa7b8cf435dfafbb, 53 | 0x14b34cf69dc25d68, 54 | ]); 55 | const G1_A: Fq = Fq::zero(); 56 | const G1_B: Fq = Fq([ 57 | 0xdd7056026000005a, 58 | 0x223fa97acb319311, 59 | 0xcc388229877910c0, 60 | 0x034394632b724eaa, 61 | ]); 62 | 63 | impl group::cofactor::CofactorGroup for G1 { 64 | type Subgroup = G1; 65 | 66 | fn clear_cofactor(&self) -> Self { 67 | *self 68 | } 69 | 70 | fn into_subgroup(self) -> CtOption { 71 | CtOption::new(self, 1.into()) 72 | } 73 | 74 | fn is_torsion_free(&self) -> Choice { 75 | 1.into() 76 | } 77 | } 78 | 79 | impl G1 { 80 | const SVDW_Z: Fq = Fq::ONE; 81 | } 82 | 83 | #[cfg(test)] 84 | mod tests { 85 | use crate::grumpkin::{Fr, G1}; 86 | use crate::CurveExt; 87 | use ff::WithSmallOrderMulGroup; 88 | 89 | #[test] 90 | fn test_hash_to_curve() { 91 | crate::tests::curve::hash_to_curve_test::(); 92 | } 93 | 94 | #[test] 95 | fn test_curve() { 96 | crate::tests::curve::curve_tests::(); 97 | } 98 | 99 | #[test] 100 | fn test_endo_consistency() { 101 | let g = G1::generator(); 102 | assert_eq!(g * Fr::ZETA, g.endo()); 103 | } 104 | 105 | #[test] 106 | fn test_serialization() { 107 | crate::tests::curve::random_serialization_test::(); 108 | #[cfg(feature = "derive_serde")] 109 | crate::tests::curve::random_serde_test::(); 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/grumpkin/mod.rs: -------------------------------------------------------------------------------- 1 | mod curve; 2 | 3 | pub use crate::bn256::{Fq as Fr, Fr as Fq}; 4 | pub use curve::*; 5 | -------------------------------------------------------------------------------- /src/hash_to_curve.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::op_ref)] 2 | 3 | use ff::{Field, FromUniformBytes, PrimeField}; 4 | use pasta_curves::arithmetic::CurveExt; 5 | use static_assertions::const_assert; 6 | use subtle::{ConditionallySelectable, ConstantTimeEq}; 7 | 8 | /// Hashes over a message and writes the output to all of `buf`. 9 | /// Modified from https://github.com/zcash/pasta_curves/blob/7e3fc6a4919f6462a32b79dd226cb2587b7961eb/src/hashtocurve.rs#L11. 10 | fn hash_to_field>( 11 | method: &str, 12 | curve_id: &str, 13 | domain_prefix: &str, 14 | message: &[u8], 15 | buf: &mut [F; 2], 16 | ) { 17 | assert!(domain_prefix.len() < 256); 18 | assert!((18 + method.len() + curve_id.len() + domain_prefix.len()) < 256); 19 | 20 | // Assume that the field size is 32 bytes and k is 256, where k is defined in 21 | // . 22 | const CHUNKLEN: usize = 64; 23 | const_assert!(CHUNKLEN * 2 < 256); 24 | 25 | // Input block size of BLAKE2b. 26 | const R_IN_BYTES: usize = 128; 27 | 28 | let personal = [0u8; 16]; 29 | let empty_hasher = blake2b_simd::Params::new() 30 | .hash_length(CHUNKLEN) 31 | .personal(&personal) 32 | .to_state(); 33 | 34 | let b_0 = empty_hasher 35 | .clone() 36 | .update(&[0; R_IN_BYTES]) 37 | .update(message) 38 | .update(&[0, (CHUNKLEN * 2) as u8, 0]) 39 | .update(domain_prefix.as_bytes()) 40 | .update(b"-") 41 | .update(curve_id.as_bytes()) 42 | .update(b"_XMD:BLAKE2b_") 43 | .update(method.as_bytes()) 44 | .update(b"_RO_") 45 | .update(&[(18 + method.len() + curve_id.len() + domain_prefix.len()) as u8]) 46 | .finalize(); 47 | 48 | let b_1 = empty_hasher 49 | .clone() 50 | .update(b_0.as_array()) 51 | .update(&[1]) 52 | .update(domain_prefix.as_bytes()) 53 | .update(b"-") 54 | .update(curve_id.as_bytes()) 55 | .update(b"_XMD:BLAKE2b_") 56 | .update(method.as_bytes()) 57 | .update(b"_RO_") 58 | .update(&[(18 + method.len() + curve_id.len() + domain_prefix.len()) as u8]) 59 | .finalize(); 60 | 61 | let b_2 = { 62 | let mut empty_hasher = empty_hasher; 63 | for (l, r) in b_0.as_array().iter().zip(b_1.as_array().iter()) { 64 | empty_hasher.update(&[*l ^ *r]); 65 | } 66 | empty_hasher 67 | .update(&[2]) 68 | .update(domain_prefix.as_bytes()) 69 | .update(b"-") 70 | .update(curve_id.as_bytes()) 71 | .update(b"_XMD:BLAKE2b_") 72 | .update(method.as_bytes()) 73 | .update(b"_RO_") 74 | .update(&[(18 + method.len() + curve_id.len() + domain_prefix.len()) as u8]) 75 | .finalize() 76 | }; 77 | 78 | for (big, buf) in [b_1, b_2].iter().zip(buf.iter_mut()) { 79 | let mut little = [0u8; CHUNKLEN]; 80 | little.copy_from_slice(big.as_array()); 81 | little.reverse(); 82 | *buf = F::from_uniform_bytes(&little); 83 | } 84 | } 85 | 86 | #[allow(clippy::too_many_arguments)] 87 | pub(crate) fn svdw_map_to_curve( 88 | u: C::Base, 89 | c1: C::Base, 90 | c2: C::Base, 91 | c3: C::Base, 92 | c4: C::Base, 93 | z: C::Base, 94 | ) -> C 95 | where 96 | C: CurveExt, 97 | { 98 | let one = C::Base::ONE; 99 | let a = C::a(); 100 | let b = C::b(); 101 | 102 | // 1. tv1 = u^2 103 | let tv1 = u.square(); 104 | // 2. tv1 = tv1 * c1 105 | let tv1 = tv1 * c1; 106 | // 3. tv2 = 1 + tv1 107 | let tv2 = one + tv1; 108 | // 4. tv1 = 1 - tv1 109 | let tv1 = one - tv1; 110 | // 5. tv3 = tv1 * tv2 111 | let tv3 = tv1 * tv2; 112 | // 6. tv3 = inv0(tv3) 113 | let tv3 = tv3.invert().unwrap_or(C::Base::ZERO); 114 | // 7. tv4 = u * tv1 115 | let tv4 = u * tv1; 116 | // 8. tv4 = tv4 * tv3 117 | let tv4 = tv4 * tv3; 118 | // 9. tv4 = tv4 * c3 119 | let tv4 = tv4 * c3; 120 | // 10. x1 = c2 - tv4 121 | let x1 = c2 - tv4; 122 | // 11. gx1 = x1^2 123 | let gx1 = x1.square(); 124 | // 12. gx1 = gx1 + A 125 | let gx1 = gx1 + a; 126 | // 13. gx1 = gx1 * x1 127 | let gx1 = gx1 * x1; 128 | // 14. gx1 = gx1 + B 129 | let gx1 = gx1 + b; 130 | // 15. e1 = is_square(gx1) 131 | let e1 = gx1.sqrt().is_some(); 132 | // 16. x2 = c2 + tv4 133 | let x2 = c2 + tv4; 134 | // 17. gx2 = x2^2 135 | let gx2 = x2.square(); 136 | // 18. gx2 = gx2 + A 137 | let gx2 = gx2 + a; 138 | // 19. gx2 = gx2 * x2 139 | let gx2 = gx2 * x2; 140 | // 20. gx2 = gx2 + B 141 | let gx2 = gx2 + b; 142 | // 21. e2 = is_square(gx2) AND NOT e1 # Avoid short-circuit logic ops 143 | let e2 = gx2.sqrt().is_some() & (!e1); 144 | // 22. x3 = tv2^2 145 | let x3 = tv2.square(); 146 | // 23. x3 = x3 * tv3 147 | let x3 = x3 * tv3; 148 | // 24. x3 = x3^2 149 | let x3 = x3.square(); 150 | // 25. x3 = x3 * c4 151 | let x3 = x3 * c4; 152 | // 26. x3 = x3 + Z 153 | let x3 = x3 + z; 154 | // 27. x = CMOV(x3, x1, e1) # x = x1 if gx1 is square, else x = x3 155 | let x = C::Base::conditional_select(&x3, &x1, e1); 156 | // 28. x = CMOV(x, x2, e2) # x = x2 if gx2 is square and gx1 is not 157 | let x = C::Base::conditional_select(&x, &x2, e2); 158 | // 29. gx = x^2 159 | let gx = x.square(); 160 | // 30. gx = gx + A 161 | let gx = gx + a; 162 | // 31. gx = gx * x 163 | let gx = gx * x; 164 | // 32. gx = gx + B 165 | let gx = gx + b; 166 | // 33. y = sqrt(gx) 167 | let y = gx.sqrt().unwrap(); 168 | // 34. e3 = sgn0(u) == sgn0(y) 169 | let e3 = u.is_odd().ct_eq(&y.is_odd()); 170 | // 35. y = CMOV(-y, y, e3) # Select correct sign of y 171 | let y = C::Base::conditional_select(&-y, &y, e3); 172 | // 36. return (x, y) 173 | C::new_jacobian(x, y, one).unwrap() 174 | } 175 | 176 | /// Implementation of https://www.ietf.org/id/draft-irtf-cfrg-hash-to-curve-16.html#name-shallue-van-de-woestijne-met 177 | #[allow(clippy::type_complexity)] 178 | pub(crate) fn svdw_hash_to_curve<'a, C>( 179 | curve_id: &'static str, 180 | domain_prefix: &'a str, 181 | z: C::Base, 182 | ) -> Box C + 'a> 183 | where 184 | C: CurveExt, 185 | C::Base: FromUniformBytes<64>, 186 | { 187 | let [c1, c2, c3, c4] = svdw_precomputed_constants::(z); 188 | 189 | Box::new(move |message| { 190 | let mut us = [C::Base::ZERO; 2]; 191 | hash_to_field("SVDW", curve_id, domain_prefix, message, &mut us); 192 | 193 | let [q0, q1]: [C; 2] = us.map(|u| svdw_map_to_curve(u, c1, c2, c3, c4, z)); 194 | 195 | let r = q0 + &q1; 196 | debug_assert!(bool::from(r.is_on_curve())); 197 | r 198 | }) 199 | } 200 | 201 | pub(crate) fn svdw_precomputed_constants(z: C::Base) -> [C::Base; 4] { 202 | let a = C::a(); 203 | let b = C::b(); 204 | let one = C::Base::ONE; 205 | let three = one + one + one; 206 | let four = three + one; 207 | let tmp = three * z.square() + four * a; 208 | 209 | // 1. c1 = g(Z) 210 | let c1 = (z.square() + a) * z + b; 211 | // 2. c2 = -Z / 2 212 | let c2 = -z * C::Base::TWO_INV; 213 | // 3. c3 = sqrt(-g(Z) * (3 * Z^2 + 4 * A)) # sgn0(c3) MUST equal 0 214 | let c3 = { 215 | let c3 = (-c1 * tmp).sqrt().unwrap(); 216 | C::Base::conditional_select(&c3, &-c3, c3.is_odd()) 217 | }; 218 | // 4. c4 = -4 * g(Z) / (3 * Z^2 + 4 * A) 219 | let c4 = -four * c1 * tmp.invert().unwrap(); 220 | 221 | [c1, c2, c3, c4] 222 | } 223 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod arithmetic; 2 | mod ff_inverse; 3 | mod ff_jacobi; 4 | 5 | pub mod hash_to_curve; 6 | // TODO: remove this and use traits defined in axiom_pairing instead. 7 | pub mod pairing; 8 | pub mod serde; 9 | 10 | pub mod bls12_381; 11 | pub mod bn256; 12 | pub mod grumpkin; 13 | pub mod pasta; 14 | pub mod secp256k1; 15 | pub mod secp256r1; 16 | 17 | #[macro_use] 18 | mod derive; 19 | pub use arithmetic::CurveAffineExt; 20 | pub use pasta_curves::arithmetic::{Coordinates, CurveAffine, CurveExt}; 21 | 22 | // Re-export ff and group to simplify down stream dependencies 23 | #[cfg(feature = "reexport")] 24 | pub use ff; 25 | #[cfg(not(feature = "reexport"))] 26 | use ff; 27 | #[cfg(feature = "reexport")] 28 | pub use group; 29 | #[cfg(not(feature = "reexport"))] 30 | use group; 31 | 32 | #[cfg(test)] 33 | pub mod tests; 34 | 35 | #[cfg(all(feature = "prefetch", target_arch = "x86_64"))] 36 | #[inline(always)] 37 | pub fn prefetch(data: &[T], offset: usize) { 38 | use core::arch::x86_64::_mm_prefetch; 39 | unsafe { 40 | _mm_prefetch( 41 | data.as_ptr().add(offset) as *const i8, 42 | core::arch::x86_64::_MM_HINT_T0, 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/pairing.rs: -------------------------------------------------------------------------------- 1 | use crate::CurveAffine; 2 | use core::ops::Mul; 3 | use ff::PrimeField; 4 | use group::{ 5 | prime::PrimeCurve, Group, GroupOps, GroupOpsOwned, ScalarMul, ScalarMulOwned, 6 | UncompressedEncoding, 7 | }; 8 | 9 | pub trait Engine: Sized + 'static + Clone { 10 | /// This is the scalar field of the engine's groups. 11 | type Scalar: PrimeField; 12 | 13 | /// The projective representation of an element in G1. 14 | type G1: PrimeCurve 15 | + From 16 | + GroupOps 17 | + GroupOpsOwned 18 | + ScalarMul 19 | + ScalarMulOwned 20 | + Group; 21 | 22 | /// The affine representation of an element in G1. 23 | type G1Affine: PairingCurveAffine< 24 | ScalarExt = Self::Scalar, 25 | CurveExt = Self::G1, 26 | Pair = Self::G2Affine, 27 | PairingResult = Self::Gt, 28 | > + From 29 | + Mul 30 | + for<'a> Mul<&'a Self::Scalar, Output = Self::G1>; 31 | 32 | /// The projective representation of an element in G2. 33 | type G2: PrimeCurve 34 | + From 35 | + GroupOps 36 | + GroupOpsOwned 37 | + ScalarMul 38 | + ScalarMulOwned; 39 | 40 | /// The affine representation of an element in G2. 41 | type G2Affine: PairingCurveAffine< 42 | ScalarExt = Self::Scalar, 43 | CurveExt = Self::G2, 44 | Pair = Self::G1Affine, 45 | PairingResult = Self::Gt, 46 | > + From 47 | + Mul 48 | + for<'a> Mul<&'a Self::Scalar, Output = Self::G2>; 49 | 50 | /// The extension field that hosts the target group of the pairing. 51 | type Gt: Group + ScalarMul + ScalarMulOwned; 52 | 53 | /// Invoke the pairing function `G1 x G2 -> Gt` without the use of precomputation and 54 | /// other optimizations. 55 | fn pairing(p: &Self::G1Affine, q: &Self::G2Affine) -> Self::Gt; 56 | } 57 | 58 | /// Affine representation of an elliptic curve point that can be used 59 | /// to perform pairings. 60 | pub trait PairingCurveAffine: CurveAffine + UncompressedEncoding { 61 | type Pair: PairingCurveAffine; 62 | type PairingResult: Group; 63 | 64 | /// Perform a pairing 65 | fn pairing_with(&self, other: &Self::Pair) -> Self::PairingResult; 66 | } 67 | 68 | /// An engine that can compute sums of pairings in an efficient way. 69 | pub trait MultiMillerLoop: Engine { 70 | /// The prepared form of `Self::G2Affine`. 71 | type G2Prepared: Clone + Send + Sync + From; 72 | 73 | /// The type returned by `Engine::miller_loop`. 74 | type Result: MillerLoopResult; 75 | 76 | /// Computes $$\sum_{i=1}^n \textbf{ML}(a_i, b_i)$$ given a series of terms 77 | /// $$(a_1, b_1), (a_2, b_2), ..., (a_n, b_n).$$ 78 | fn multi_miller_loop(terms: &[(&Self::G1Affine, &Self::G2Prepared)]) -> Self::Result; 79 | } 80 | 81 | /// Represents results of a Miller loop, one of the most expensive portions of the pairing 82 | /// function. 83 | /// 84 | /// `MillerLoopResult`s cannot be compared with each other until 85 | /// [`MillerLoopResult::final_exponentiation`] is called, which is also expensive. 86 | pub trait MillerLoopResult { 87 | /// The extension field that hosts the target group of the pairing. 88 | type Gt: Group; 89 | 90 | /// This performs a "final exponentiation" routine to convert the result of a Miller 91 | /// loop into an element of [`MillerLoopResult::Gt`], so that it can be compared with 92 | /// other elements of `Gt`. 93 | fn final_exponentiation(&self) -> Self::Gt; 94 | } 95 | -------------------------------------------------------------------------------- /src/pasta/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::mul_512; 2 | use crate::arithmetic::sbb; 3 | use crate::{ 4 | arithmetic::{CurveEndo, EndoParameters}, 5 | endo, 6 | }; 7 | use ff::PrimeField; 8 | use ff::WithSmallOrderMulGroup; 9 | pub use pasta_curves::{pallas, vesta, Ep, EpAffine, Eq, EqAffine, Fp, Fq}; 10 | use std::convert::TryInto; 11 | 12 | impl crate::CurveAffineExt for EpAffine { 13 | fn batch_add( 14 | _: &mut [Self], 15 | _: &[u32], 16 | _: usize, 17 | _: usize, 18 | _: &[Self], 19 | _: &[u32], 20 | ) { 21 | unimplemented!(); 22 | } 23 | } 24 | 25 | impl crate::CurveAffineExt for EqAffine { 26 | fn batch_add( 27 | _: &mut [Self], 28 | _: &[u32], 29 | _: usize, 30 | _: usize, 31 | _: &[Self], 32 | _: &[u32], 33 | ) { 34 | unimplemented!(); 35 | } 36 | } 37 | 38 | // Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go 39 | // with `pasta_curves::Fp::ZETA` 40 | // See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md 41 | // to have more details about the endomorphism. 42 | const ENDO_PARAMS_EQ: EndoParameters = EndoParameters { 43 | // round(b2/n) 44 | gamma1: [0x32c49e4c00000003, 0x279a745902a2654e, 0x1, 0x0], 45 | // round(-b1/n) 46 | gamma2: [0x31f0256800000002, 0x4f34e8b2066389a4, 0x2, 0x0], 47 | b1: [0x8cb1279300000001, 0x49e69d1640a89953, 0x0, 0x0], 48 | b2: [0x0c7c095a00000001, 0x93cd3a2c8198e269, 0x0, 0x0], 49 | }; 50 | 51 | // Generated using https://github.com/ConsenSys/gnark-crypto/blob/master/ecc/utils.go 52 | // with `pasta_curves::Fq::ZETA` 53 | // See https://github.com/demining/Endomorphism-Secp256k1/blob/main/README.md 54 | // to have more details about the endomorphism. 55 | const ENDO_PARAMS_EP: EndoParameters = EndoParameters { 56 | // round(b2/n) 57 | gamma1: [0x32c49e4bffffffff, 0x279a745902a2654e, 0x1, 0x0], 58 | // round(-b1/n) 59 | gamma2: [0x31f0256800000002, 0x4f34e8b2066389a4, 0x2, 0x0], 60 | b1: [0x8cb1279300000000, 0x49e69d1640a89953, 0x0, 0x0], 61 | b2: [0x0c7c095a00000001, 0x93cd3a2c8198e269, 0x0, 0x0], 62 | }; 63 | 64 | endo!(Eq, Fp, ENDO_PARAMS_EQ); 65 | endo!(Ep, Fq, ENDO_PARAMS_EP); 66 | 67 | #[test] 68 | fn test_endo() { 69 | use ff::Field; 70 | use rand_core::OsRng; 71 | 72 | for _ in 0..100000 { 73 | let k = Fp::random(OsRng); 74 | let (k1, k1_neg, k2, k2_neg) = Eq::decompose_scalar(&k); 75 | if k1_neg & k2_neg { 76 | assert_eq!(k, -Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) 77 | } else if k1_neg { 78 | assert_eq!(k, -Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) 79 | } else if k2_neg { 80 | assert_eq!(k, Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) 81 | } else { 82 | assert_eq!(k, Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) 83 | } 84 | } 85 | 86 | for _ in 0..100000 { 87 | let k = Fp::random(OsRng); 88 | let (k1, k1_neg, k2, k2_neg) = Eq::decompose_scalar(&k); 89 | if k1_neg & k2_neg { 90 | assert_eq!(k, -Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) 91 | } else if k1_neg { 92 | assert_eq!(k, -Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) 93 | } else if k2_neg { 94 | assert_eq!(k, Fp::from_u128(k1) + Fp::ZETA * Fp::from_u128(k2)) 95 | } else { 96 | assert_eq!(k, Fp::from_u128(k1) - Fp::ZETA * Fp::from_u128(k2)) 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/secp256k1/curve.rs: -------------------------------------------------------------------------------- 1 | use crate::ff::WithSmallOrderMulGroup; 2 | use crate::ff::{Field, PrimeField}; 3 | use crate::group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}; 4 | use crate::secp256k1::Fp; 5 | use crate::secp256k1::Fq; 6 | use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt}; 7 | use core::cmp; 8 | use core::fmt::Debug; 9 | use core::iter::Sum; 10 | use core::ops::{Add, Mul, Neg, Sub}; 11 | use rand::RngCore; 12 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 13 | 14 | #[cfg(feature = "derive_serde")] 15 | use serde::{Deserialize, Serialize}; 16 | 17 | impl group::cofactor::CofactorGroup for Secp256k1 { 18 | type Subgroup = Secp256k1; 19 | 20 | fn clear_cofactor(&self) -> Self { 21 | *self 22 | } 23 | 24 | fn into_subgroup(self) -> CtOption { 25 | CtOption::new(self, 1.into()) 26 | } 27 | 28 | fn is_torsion_free(&self) -> Choice { 29 | 1.into() 30 | } 31 | } 32 | 33 | // Reference: https://neuromancer.sk/std/secg/secp256k1 34 | const SECP_GENERATOR_X: Fp = Fp::from_raw([ 35 | 0x59F2815B16F81798, 36 | 0x029BFCDB2DCE28D9, 37 | 0x55A06295CE870B07, 38 | 0x79BE667EF9DCBBAC, 39 | ]); 40 | const SECP_GENERATOR_Y: Fp = Fp::from_raw([ 41 | 0x9C47D08FFB10D4B8, 42 | 0xFD17B448A6855419, 43 | 0x5DA4FBFC0E1108A8, 44 | 0x483ADA7726A3C465, 45 | ]); 46 | 47 | const SECP_A: Fp = Fp::from_raw([0, 0, 0, 0]); 48 | const SECP_B: Fp = Fp::from_raw([7, 0, 0, 0]); 49 | 50 | use crate::{ 51 | batch_add, impl_add_binop_specify_output, impl_binops_additive, 52 | impl_binops_additive_specify_output, impl_binops_multiplicative, 53 | impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, new_curve_impl, 54 | }; 55 | 56 | new_curve_impl!( 57 | (pub), 58 | Secp256k1, 59 | Secp256k1Affine, 60 | true, 61 | Fp, 62 | Fq, 63 | (SECP_GENERATOR_X,SECP_GENERATOR_Y), 64 | SECP_A, 65 | SECP_B, 66 | "secp256k1", 67 | |_, _| unimplemented!(), 68 | ); 69 | 70 | impl CurveAffineExt for Secp256k1Affine { 71 | batch_add!(); 72 | 73 | fn into_coordinates(self) -> (Self::Base, Self::Base) { 74 | (self.x, self.y) 75 | } 76 | } 77 | 78 | #[test] 79 | fn test_curve() { 80 | crate::tests::curve::curve_tests::(); 81 | } 82 | 83 | #[test] 84 | fn test_serialization() { 85 | crate::tests::curve::random_serialization_test::(); 86 | #[cfg(feature = "derive_serde")] 87 | crate::tests::curve::random_serde_test::(); 88 | } 89 | 90 | #[test] 91 | fn test_endo_consistency() { 92 | let g = Secp256k1::generator(); 93 | assert_eq!(g * Fq::ZETA, g.endo()); 94 | } 95 | 96 | #[test] 97 | fn ecdsa_example() { 98 | use crate::group::Curve; 99 | use crate::CurveAffine; 100 | use ff::FromUniformBytes; 101 | use rand_core::OsRng; 102 | 103 | fn mod_n(x: Fp) -> Fq { 104 | let mut x_repr = [0u8; 32]; 105 | x_repr.copy_from_slice(x.to_repr().as_ref()); 106 | let mut x_bytes = [0u8; 64]; 107 | x_bytes[..32].copy_from_slice(&x_repr[..]); 108 | Fq::from_uniform_bytes(&x_bytes) 109 | } 110 | 111 | let g = Secp256k1::generator(); 112 | 113 | for _ in 0..1000 { 114 | // Generate a key pair 115 | let sk = Fq::random(OsRng); 116 | let pk = (g * sk).to_affine(); 117 | 118 | // Generate a valid signature 119 | // Suppose `m_hash` is the message hash 120 | let msg_hash = Fq::random(OsRng); 121 | 122 | let (r, s) = { 123 | // Draw arandomness 124 | let k = Fq::random(OsRng); 125 | let k_inv = k.invert().unwrap(); 126 | 127 | // Calculate `r` 128 | let r_point = (g * k).to_affine().coordinates().unwrap(); 129 | let x = r_point.x(); 130 | let r = mod_n(*x); 131 | 132 | // Calculate `s` 133 | let s = k_inv * (msg_hash + (r * sk)); 134 | 135 | (r, s) 136 | }; 137 | 138 | { 139 | // Verify 140 | let s_inv = s.invert().unwrap(); 141 | let u_1 = msg_hash * s_inv; 142 | let u_2 = r * s_inv; 143 | 144 | let v_1 = g * u_1; 145 | let v_2 = pk * u_2; 146 | 147 | let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); 148 | let x_candidate = r_point.x(); 149 | let r_candidate = mod_n(*x_candidate); 150 | 151 | assert_eq!(r, r_candidate); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/secp256k1/fp.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::{adc, mac, sbb}; 2 | use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; 3 | use crate::{ 4 | field_arithmetic, field_bits, field_common, field_specific, impl_add_binop_specify_output, 5 | impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, 6 | impl_binops_multiplicative_mixed, impl_from_u64, impl_sub_binop_specify_output, impl_sum_prod, 7 | }; 8 | use core::convert::TryInto; 9 | use core::fmt; 10 | use core::ops::{Add, Mul, Neg, Sub}; 11 | use rand::RngCore; 12 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 13 | 14 | #[cfg(feature = "derive_serde")] 15 | use serde::{Deserialize, Serialize}; 16 | 17 | /// This represents an element of $\mathbb{F}_p$ where 18 | /// 19 | /// `p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f` 20 | /// 21 | /// is the base field of the secp256k1 curve. 22 | // The internal representation of this type is four 64-bit unsigned 23 | // integers in little-endian order. `Fp` values are always in 24 | // Montgomery form; i.e., Fp(a) = aR mod p, with R = 2^256. 25 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 26 | #[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] 27 | pub struct Fp(pub(crate) [u64; 4]); 28 | 29 | /// Constant representing the modulus 30 | /// p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f 31 | const MODULUS: Fp = Fp([ 32 | 0xfffffffefffffc2f, 33 | 0xffffffffffffffff, 34 | 0xffffffffffffffff, 35 | 0xffffffffffffffff, 36 | ]); 37 | 38 | /// Constant representing the multiplicative generator of the modulus. 39 | /// It's derived with SageMath with: `GF(MODULUS).primitive_element()`. 40 | const MULTIPLICATIVE_GENERATOR: Fp = Fp::from_raw([0x03, 0x00, 0x00, 0x00]); 41 | 42 | /// The modulus as u32 limbs. 43 | #[cfg(not(target_pointer_width = "64"))] 44 | const MODULUS_LIMBS_32: [u32; 8] = [ 45 | 0xffff_fc2f, 46 | 0xffff_fffe, 47 | 0xffff_ffff, 48 | 0xffff_ffff, 49 | 0xffff_ffff, 50 | 0xffff_ffff, 51 | 0xffff_ffff, 52 | 0xffff_ffff, 53 | ]; 54 | 55 | /// Constant representing the modolus as static str 56 | const MODULUS_STR: &str = "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f"; 57 | 58 | /// INV = -(p^{-1} mod 2^64) mod 2^64 59 | const INV: u64 = 0xd838091dd2253531; 60 | 61 | /// R = 2^256 mod p 62 | /// 0x1000003d1 63 | const R: Fp = Fp([0x1000003d1, 0, 0, 0]); 64 | 65 | /// R^2 = 2^512 mod p 66 | /// 0x1000007a2000e90a1 67 | const R2: Fp = Fp([0x000007a2000e90a1, 0x1, 0, 0]); 68 | 69 | /// R^3 = 2^768 mod p 70 | /// 0x100000b73002bb1e33795f671 71 | const R3: Fp = Fp([0x002bb1e33795f671, 0x100000b73, 0, 0]); 72 | 73 | /// 1 / 2 mod p 74 | const TWO_INV: Fp = Fp::from_raw([ 75 | 0xffffffff7ffffe18, 76 | 0xffffffffffffffff, 77 | 0xffffffffffffffff, 78 | 0x7fffffffffffffff, 79 | ]); 80 | 81 | const ZETA: Fp = Fp::from_raw([ 82 | 0xc1396c28719501ee, 83 | 0x9cf0497512f58995, 84 | 0x6e64479eac3434e9, 85 | 0x7ae96a2b657c0710, 86 | ]); 87 | 88 | /// Generator of the t-order multiplicative subgroup. 89 | /// Computed by exponentiating Self::MULTIPLICATIVE_GENERATOR by 2^s, where s is Self::S. 90 | /// `0x0000000000000000000000000000000000000000000000000000000000000009`. 91 | const DELTA: Fp = Fp([0x900002259u64, 0, 0, 0]); 92 | 93 | /// Implementations of this trait MUST ensure that this is the generator used to derive Self::ROOT_OF_UNITY. 94 | /// Derived from: 95 | /// ```ignore 96 | /// Zp(Zp(mul_generator)^t) where t = (modulus - 1 )/ 2 97 | /// 115792089237316195423570985008687907853269984665640564039457584007908834671662 98 | /// ``` 99 | const ROOT_OF_UNITY: Fp = Fp([ 100 | 0xfffffffdfffff85eu64, 101 | 0xffffffffffffffffu64, 102 | 0xffffffffffffffffu64, 103 | 0xffffffffffffffffu64, 104 | ]); 105 | 106 | /// Inverse of [`ROOT_OF_UNITY`]. 107 | const ROOT_OF_UNITY_INV: Fp = Fp([ 108 | 0xfffffffdfffff85eu64, 109 | 0xffffffffffffffffu64, 110 | 0xffffffffffffffffu64, 111 | 0xffffffffffffffffu64, 112 | ]); 113 | 114 | impl_binops_additive!(Fp, Fp); 115 | impl_binops_multiplicative!(Fp, Fp); 116 | field_common!( 117 | Fp, 118 | MODULUS, 119 | INV, 120 | MODULUS_STR, 121 | TWO_INV, 122 | ROOT_OF_UNITY_INV, 123 | DELTA, 124 | ZETA, 125 | R, 126 | R2, 127 | R3 128 | ); 129 | impl_from_u64!(Fp, R2); 130 | field_arithmetic!(Fp, MODULUS, INV, dense); 131 | impl_sum_prod!(Fp); 132 | 133 | #[cfg(target_pointer_width = "64")] 134 | field_bits!(Fp, MODULUS); 135 | #[cfg(not(target_pointer_width = "64"))] 136 | field_bits!(Fp, MODULUS, MODULUS_LIMBS_32); 137 | 138 | impl Fp { 139 | pub const fn size() -> usize { 140 | 32 141 | } 142 | } 143 | 144 | impl ff::Field for Fp { 145 | const ZERO: Self = Self::zero(); 146 | const ONE: Self = Self::one(); 147 | 148 | fn random(mut rng: impl RngCore) -> Self { 149 | Self::from_u512([ 150 | rng.next_u64(), 151 | rng.next_u64(), 152 | rng.next_u64(), 153 | rng.next_u64(), 154 | rng.next_u64(), 155 | rng.next_u64(), 156 | rng.next_u64(), 157 | rng.next_u64(), 158 | ]) 159 | } 160 | 161 | fn double(&self) -> Self { 162 | self.double() 163 | } 164 | 165 | #[inline(always)] 166 | fn square(&self) -> Self { 167 | self.square() 168 | } 169 | 170 | /// Computes the square root of this element, if it exists. 171 | fn sqrt(&self) -> CtOption { 172 | let tmp = self.pow([ 173 | 0xffffffffbfffff0c, 174 | 0xffffffffffffffff, 175 | 0xffffffffffffffff, 176 | 0x3fffffffffffffff, 177 | ]); 178 | 179 | CtOption::new(tmp, tmp.square().ct_eq(self)) 180 | } 181 | 182 | /// Returns the multiplicative inverse of the 183 | /// element. If it is zero, the method fails. 184 | fn invert(&self) -> CtOption { 185 | self.invert() 186 | } 187 | 188 | fn pow_vartime>(&self, exp: S) -> Self { 189 | let mut res = Self::one(); 190 | let mut found_one = false; 191 | for e in exp.as_ref().iter().rev() { 192 | for i in (0..64).rev() { 193 | if found_one { 194 | res = res.square(); 195 | } 196 | 197 | if ((*e >> i) & 1) == 1 { 198 | found_one = true; 199 | res *= self; 200 | } 201 | } 202 | } 203 | res 204 | } 205 | 206 | fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { 207 | ff::helpers::sqrt_ratio_generic(num, div) 208 | } 209 | } 210 | 211 | impl ff::PrimeField for Fp { 212 | type Repr = [u8; 32]; 213 | 214 | const MODULUS: &'static str = MODULUS_STR; 215 | const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; 216 | const TWO_INV: Self = TWO_INV; 217 | const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; 218 | const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; 219 | const DELTA: Self = DELTA; 220 | const NUM_BITS: u32 = 256; 221 | const CAPACITY: u32 = 255; 222 | const S: u32 = 1; 223 | 224 | fn from_repr(repr: Self::Repr) -> CtOption { 225 | let mut tmp = Fp([0, 0, 0, 0]); 226 | 227 | tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); 228 | tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); 229 | tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); 230 | tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); 231 | 232 | // Try to subtract the modulus 233 | let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); 234 | let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); 235 | let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); 236 | let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); 237 | 238 | // If the element is smaller than MODULUS then the 239 | // subtraction will underflow, producing a borrow value 240 | // of 0xffff...ffff. Otherwise, it'll be zero. 241 | let is_some = (borrow as u8) & 1; 242 | 243 | // Convert to Montgomery form by computing 244 | // (a.R^0 * R^2) / R = a.R 245 | tmp *= &R2; 246 | 247 | CtOption::new(tmp, Choice::from(is_some)) 248 | } 249 | 250 | fn to_repr(&self) -> Self::Repr { 251 | // Turn into canonical form by computing 252 | // (a.R) / R = a 253 | let tmp = Fp::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); 254 | 255 | let mut res = [0; 32]; 256 | res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); 257 | res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); 258 | res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); 259 | res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); 260 | 261 | res 262 | } 263 | 264 | fn from_u128(v: u128) -> Self { 265 | Self::from_raw([v as u64, (v >> 64) as u64, 0, 0]) 266 | } 267 | 268 | fn is_odd(&self) -> Choice { 269 | Choice::from(self.to_repr()[0] & 1) 270 | } 271 | } 272 | 273 | impl FromUniformBytes<64> for Fp { 274 | /// Converts a 512-bit little endian integer into 275 | /// an `Fp` by reducing by the modulus. 276 | fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { 277 | Self::from_u512([ 278 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 279 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 280 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 281 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 282 | u64::from_le_bytes(bytes[32..40].try_into().unwrap()), 283 | u64::from_le_bytes(bytes[40..48].try_into().unwrap()), 284 | u64::from_le_bytes(bytes[48..56].try_into().unwrap()), 285 | u64::from_le_bytes(bytes[56..64].try_into().unwrap()), 286 | ]) 287 | } 288 | } 289 | 290 | impl WithSmallOrderMulGroup<3> for Fp { 291 | const ZETA: Self = ZETA; 292 | } 293 | 294 | #[cfg(test)] 295 | mod test { 296 | use super::*; 297 | use ff::Field; 298 | use rand_core::OsRng; 299 | 300 | #[test] 301 | fn test_sqrt() { 302 | // NB: TWO_INV is standing in as a "random" field element 303 | let v = (Fp::TWO_INV).square().sqrt().unwrap(); 304 | assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV); 305 | 306 | for _ in 0..10000 { 307 | let a = Fp::random(OsRng); 308 | let mut b = a; 309 | b = b.square(); 310 | 311 | let b = b.sqrt().unwrap(); 312 | let mut negb = b; 313 | negb = negb.neg(); 314 | 315 | assert!(a == b || a == negb); 316 | } 317 | } 318 | 319 | #[test] 320 | fn test_constants() { 321 | assert_eq!( 322 | Fp::MODULUS, 323 | "0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f", 324 | ); 325 | 326 | assert_eq!(Fp::from(2) * Fp::TWO_INV, Fp::ONE); 327 | } 328 | 329 | #[test] 330 | fn test_delta() { 331 | assert_eq!(Fp::DELTA, MULTIPLICATIVE_GENERATOR.pow([1u64 << Fp::S])); 332 | } 333 | 334 | #[test] 335 | fn test_root_of_unity() { 336 | assert_eq!(Fp::ROOT_OF_UNITY.pow_vartime([1 << Fp::S]), Fp::one()); 337 | } 338 | 339 | #[test] 340 | fn test_inv_root_of_unity() { 341 | assert_eq!(Fp::ROOT_OF_UNITY_INV, Fp::ROOT_OF_UNITY.invert().unwrap()); 342 | } 343 | 344 | #[test] 345 | fn test_field() { 346 | crate::tests::field::random_field_tests::("secp256k1 base".to_string()); 347 | } 348 | 349 | #[test] 350 | #[cfg(feature = "bits")] 351 | fn test_bits() { 352 | crate::tests::field::random_bits_tests::("secp256k1 base".to_string()); 353 | } 354 | 355 | #[test] 356 | fn test_serialization() { 357 | crate::tests::field::random_serialization_test::("secp256k1 base".to_string()); 358 | #[cfg(feature = "derive_serde")] 359 | crate::tests::field::random_serde_test::("secp256k1 base".to_string()); 360 | } 361 | } 362 | -------------------------------------------------------------------------------- /src/secp256k1/fq.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::{adc, mac, sbb}; 2 | use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; 3 | use crate::{ 4 | field_arithmetic, field_bits, field_common, field_specific, impl_add_binop_specify_output, 5 | impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, 6 | impl_binops_multiplicative_mixed, impl_from_u64, impl_sub_binop_specify_output, impl_sum_prod, 7 | }; 8 | use core::convert::TryInto; 9 | use core::fmt; 10 | use core::ops::{Add, Mul, Neg, Sub}; 11 | use rand::RngCore; 12 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 13 | 14 | #[cfg(feature = "derive_serde")] 15 | use serde::{Deserialize, Serialize}; 16 | 17 | /// This represents an element of $\mathbb{F}_q$ where 18 | /// 19 | /// `q = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141` 20 | /// 21 | /// is the scalar field of the secp256k1 curve. 22 | // The internal representation of this type is four 64-bit unsigned 23 | // integers in little-endian order. `Fq` values are always in 24 | // Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256. 25 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 26 | #[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] 27 | pub struct Fq(pub(crate) [u64; 4]); 28 | 29 | /// Constant representing the modulus 30 | /// q = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 31 | const MODULUS: Fq = Fq([ 32 | 0xbfd25e8cd0364141, 33 | 0xbaaedce6af48a03b, 34 | 0xfffffffffffffffe, 35 | 0xffffffffffffffff, 36 | ]); 37 | 38 | /// The modulus as u32 limbs. 39 | #[cfg(not(target_pointer_width = "64"))] 40 | const MODULUS_LIMBS_32: [u32; 8] = [ 41 | 0xd036_4141, 42 | 0xbfd2_5e8c, 43 | 0xaf48_a03b, 44 | 0xbaae_dce6, 45 | 0xffff_fffe, 46 | 0xffff_ffff, 47 | 0xffff_ffff, 48 | 0xffff_ffff, 49 | ]; 50 | 51 | ///Constant representing the modulus as static str 52 | const MODULUS_STR: &str = "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141"; 53 | 54 | /// INV = -(q^{-1} mod 2^64) mod 2^64 55 | const INV: u64 = 0x4b0dff665588b13f; 56 | 57 | /// R = 2^256 mod q 58 | /// 0x14551231950b75fc4402da1732fc9bebf 59 | const R: Fq = Fq([0x402da1732fc9bebf, 0x4551231950b75fc4, 0x1, 0]); 60 | 61 | /// R^2 = 2^512 mod q 62 | /// 0x9d671cd581c69bc5e697f5e45bcd07c6741496c20e7cf878896cf21467d7d140 63 | const R2: Fq = Fq([ 64 | 0x896cf21467d7d140, 65 | 0x741496c20e7cf878, 66 | 0xe697f5e45bcd07c6, 67 | 0x9d671cd581c69bc5, 68 | ]); 69 | 70 | /// R^3 = 2^768 mod q 71 | /// 0x555d800c18ef116db1b31347f1d0b2da0017648444d4322c7bc0cfe0e9ff41ed 72 | const R3: Fq = Fq([ 73 | 0x7bc0cfe0e9ff41ed, 74 | 0x0017648444d4322c, 75 | 0xb1b31347f1d0b2da, 76 | 0x555d800c18ef116d, 77 | ]); 78 | 79 | /// `GENERATOR = 7 mod r` is a generator of the `q - 1` order multiplicative 80 | /// subgroup, or in other words a primitive root of the field. 81 | /// It's derived with SageMath with: `GF(MODULUS).primitive_element()`. 82 | const GENERATOR: Fq = Fq::from_raw([0x07, 0x00, 0x00, 0x00]); 83 | 84 | /// GENERATOR^t where t * 2^s + 1 = r with t odd. In other words, this is a 2^s root of unity. 85 | /// `0xc1dc060e7a91986df9879a3fbc483a898bdeab680756045992f4b5402b052f2` 86 | const ROOT_OF_UNITY: Fq = Fq::from_raw([ 87 | 0x992f4b5402b052f2, 88 | 0x98bdeab680756045, 89 | 0xdf9879a3fbc483a8, 90 | 0xc1dc060e7a91986, 91 | ]); 92 | 93 | /// 1 / ROOT_OF_UNITY mod q 94 | const ROOT_OF_UNITY_INV: Fq = Fq::from_raw([ 95 | 0xb6fb30a0884f0d1c, 96 | 0x77a275910aa413c3, 97 | 0xefc7b0c75b8cbb72, 98 | 0xfd3ae181f12d7096, 99 | ]); 100 | 101 | /// 1 / 2 mod q 102 | const TWO_INV: Fq = Fq::from_raw([ 103 | 0xdfe92f46681b20a1, 104 | 0x5d576e7357a4501d, 105 | 0xffffffffffffffff, 106 | 0x7fffffffffffffff, 107 | ]); 108 | 109 | const ZETA: Fq = Fq::from_raw([ 110 | 0xdf02967c1b23bd72, 111 | 0x122e22ea20816678, 112 | 0xa5261c028812645a, 113 | 0x5363ad4cc05c30e0, 114 | ]); 115 | 116 | /// Generator of the t-order multiplicative subgroup. 117 | /// Computed by exponentiating Self::MULTIPLICATIVE_GENERATOR by 2^s, where s is Self::S. 118 | /// `0x0000000000000000000cbc21fe4561c8d63b78e780e1341e199417c8c0bb7601` 119 | const DELTA: Fq = Fq([ 120 | 0xd91b33d24319d9e8, 121 | 0xb81c6596ff5d6740, 122 | 0xa463969ca14c51c1, 123 | 0x1900960de4b7929c, 124 | ]); 125 | 126 | impl_binops_additive!(Fq, Fq); 127 | impl_binops_multiplicative!(Fq, Fq); 128 | field_common!( 129 | Fq, 130 | MODULUS, 131 | INV, 132 | MODULUS_STR, 133 | TWO_INV, 134 | ROOT_OF_UNITY_INV, 135 | DELTA, 136 | ZETA, 137 | R, 138 | R2, 139 | R3 140 | ); 141 | impl_from_u64!(Fq, R2); 142 | field_arithmetic!(Fq, MODULUS, INV, dense); 143 | impl_sum_prod!(Fq); 144 | 145 | #[cfg(target_pointer_width = "64")] 146 | field_bits!(Fq, MODULUS); 147 | #[cfg(not(target_pointer_width = "64"))] 148 | field_bits!(Fq, MODULUS, MODULUS_LIMBS_32); 149 | 150 | impl Fq { 151 | pub const fn size() -> usize { 152 | 32 153 | } 154 | } 155 | 156 | impl ff::Field for Fq { 157 | const ZERO: Self = Self::zero(); 158 | const ONE: Self = Self::one(); 159 | 160 | fn random(mut rng: impl RngCore) -> Self { 161 | Self::from_u512([ 162 | rng.next_u64(), 163 | rng.next_u64(), 164 | rng.next_u64(), 165 | rng.next_u64(), 166 | rng.next_u64(), 167 | rng.next_u64(), 168 | rng.next_u64(), 169 | rng.next_u64(), 170 | ]) 171 | } 172 | 173 | fn double(&self) -> Self { 174 | self.double() 175 | } 176 | 177 | #[inline(always)] 178 | fn square(&self) -> Self { 179 | self.square() 180 | } 181 | 182 | /// Returns the multiplicative inverse of the 183 | /// element. If it is zero, the method fails. 184 | fn invert(&self) -> CtOption { 185 | self.invert() 186 | } 187 | 188 | fn pow_vartime>(&self, exp: S) -> Self { 189 | let mut res = Self::one(); 190 | let mut found_one = false; 191 | for e in exp.as_ref().iter().rev() { 192 | for i in (0..64).rev() { 193 | if found_one { 194 | res = res.square(); 195 | } 196 | 197 | if ((*e >> i) & 1) == 1 { 198 | found_one = true; 199 | res *= self; 200 | } 201 | } 202 | } 203 | res 204 | } 205 | 206 | fn sqrt(&self) -> CtOption { 207 | let tm1d2 = [ 208 | 0x777fa4bd19a06c82, 209 | 0xfd755db9cd5e9140, 210 | 0xffffffffffffffff, 211 | 0x01ffffffffffffff, 212 | ]; 213 | 214 | ff::helpers::sqrt_tonelli_shanks(self, tm1d2) 215 | } 216 | 217 | fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { 218 | ff::helpers::sqrt_ratio_generic(num, div) 219 | } 220 | } 221 | 222 | impl ff::PrimeField for Fq { 223 | type Repr = [u8; 32]; 224 | 225 | const NUM_BITS: u32 = 256; 226 | const CAPACITY: u32 = 255; 227 | const MODULUS: &'static str = MODULUS_STR; 228 | const MULTIPLICATIVE_GENERATOR: Self = GENERATOR; 229 | const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; 230 | const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; 231 | const TWO_INV: Self = TWO_INV; 232 | const DELTA: Self = DELTA; 233 | const S: u32 = 6; 234 | 235 | fn from_repr(repr: Self::Repr) -> CtOption { 236 | let mut tmp = Fq([0, 0, 0, 0]); 237 | 238 | tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); 239 | tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); 240 | tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); 241 | tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); 242 | 243 | // Try to subtract the modulus 244 | let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); 245 | let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); 246 | let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); 247 | let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); 248 | 249 | // If the element is smaller than MODULUS then the 250 | // subtraction will underflow, producing a borrow value 251 | // of 0xffff...ffff. Otherwise, it'll be zero. 252 | let is_some = (borrow as u8) & 1; 253 | 254 | // Convert to Montgomery form by computing 255 | // (a.R^0 * R^2) / R = a.R 256 | tmp *= &R2; 257 | 258 | CtOption::new(tmp, Choice::from(is_some)) 259 | } 260 | 261 | fn to_repr(&self) -> Self::Repr { 262 | // Turn into canonical form by computing 263 | // (a.R) / R = a 264 | let tmp = Fq::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); 265 | 266 | let mut res = [0; 32]; 267 | res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); 268 | res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); 269 | res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); 270 | res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); 271 | 272 | res 273 | } 274 | 275 | fn is_odd(&self) -> Choice { 276 | Choice::from(self.to_repr()[0] & 1) 277 | } 278 | } 279 | 280 | impl FromUniformBytes<64> for Fq { 281 | /// Converts a 512-bit little endian integer into 282 | /// an `Fq` by reducing by the modulus. 283 | fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { 284 | Self::from_u512([ 285 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 286 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 287 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 288 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 289 | u64::from_le_bytes(bytes[32..40].try_into().unwrap()), 290 | u64::from_le_bytes(bytes[40..48].try_into().unwrap()), 291 | u64::from_le_bytes(bytes[48..56].try_into().unwrap()), 292 | u64::from_le_bytes(bytes[56..64].try_into().unwrap()), 293 | ]) 294 | } 295 | } 296 | 297 | impl WithSmallOrderMulGroup<3> for Fq { 298 | const ZETA: Self = ZETA; 299 | } 300 | 301 | #[cfg(test)] 302 | mod test { 303 | use super::*; 304 | use ff::Field; 305 | use rand_core::OsRng; 306 | 307 | #[test] 308 | fn test_sqrt() { 309 | // NB: TWO_INV is standing in as a "random" field element 310 | let v = (Fq::TWO_INV).square().sqrt().unwrap(); 311 | assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV); 312 | 313 | for _ in 0..10000 { 314 | let a = Fq::random(OsRng); 315 | let mut b = a; 316 | b = b.square(); 317 | 318 | let b = b.sqrt().unwrap(); 319 | let mut negb = b; 320 | negb = negb.neg(); 321 | 322 | assert!(a == b || a == negb); 323 | } 324 | } 325 | 326 | #[test] 327 | fn test_constants() { 328 | assert_eq!( 329 | Fq::MODULUS, 330 | "0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 331 | ); 332 | 333 | assert_eq!(Fq::from(2) * Fq::TWO_INV, Fq::ONE); 334 | } 335 | 336 | #[test] 337 | fn test_delta() { 338 | assert_eq!(Fq::DELTA, Fq::MULTIPLICATIVE_GENERATOR.pow([1u64 << Fq::S])); 339 | } 340 | 341 | #[test] 342 | fn test_root_of_unity() { 343 | assert_eq!(Fq::ROOT_OF_UNITY.pow_vartime([1 << Fq::S]), Fq::one()); 344 | } 345 | 346 | #[test] 347 | fn test_inv_root_of_unity() { 348 | assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::ROOT_OF_UNITY.invert().unwrap()); 349 | } 350 | 351 | #[test] 352 | fn test_field() { 353 | crate::tests::field::random_field_tests::("secp256k1 scalar".to_string()); 354 | } 355 | 356 | #[test] 357 | #[cfg(feature = "bits")] 358 | fn test_bits() { 359 | crate::tests::field::random_bits_tests::("secp256k1 scalar".to_string()); 360 | } 361 | 362 | #[test] 363 | fn test_serialization() { 364 | crate::tests::field::random_serialization_test::("secp256k1 scalar".to_string()); 365 | #[cfg(feature = "derive_serde")] 366 | crate::tests::field::random_serde_test::("secp256k1 scalar".to_string()); 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/secp256k1/mod.rs: -------------------------------------------------------------------------------- 1 | mod curve; 2 | mod fp; 3 | mod fq; 4 | 5 | pub use curve::*; 6 | pub use fp::*; 7 | pub use fq::*; 8 | -------------------------------------------------------------------------------- /src/secp256r1/curve.rs: -------------------------------------------------------------------------------- 1 | use crate::ff::WithSmallOrderMulGroup; 2 | use crate::ff::{Field, PrimeField}; 3 | use crate::group::{prime::PrimeCurveAffine, Curve, Group as _, GroupEncoding}; 4 | use crate::secp256r1::Fp; 5 | use crate::secp256r1::Fq; 6 | use crate::{Coordinates, CurveAffine, CurveAffineExt, CurveExt}; 7 | use core::cmp; 8 | use core::fmt::Debug; 9 | use core::iter::Sum; 10 | use core::ops::{Add, Mul, Neg, Sub}; 11 | use rand::RngCore; 12 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 13 | 14 | #[cfg(feature = "derive_serde")] 15 | use serde::{Deserialize, Serialize}; 16 | 17 | impl group::cofactor::CofactorGroup for Secp256r1 { 18 | type Subgroup = Secp256r1; 19 | 20 | fn clear_cofactor(&self) -> Self { 21 | *self 22 | } 23 | 24 | fn into_subgroup(self) -> CtOption { 25 | CtOption::new(self, 1.into()) 26 | } 27 | 28 | fn is_torsion_free(&self) -> Choice { 29 | 1.into() 30 | } 31 | } 32 | 33 | // Reference: https://neuromancer.sk/std/secg/secp256r1 34 | const SECP_GENERATOR_X: Fp = Fp::from_raw([ 35 | 0xF4A13945D898C296, 36 | 0x77037D812DEB33A0, 37 | 0xF8BCE6E563A440F2, 38 | 0x6B17D1F2E12C4247, 39 | ]); 40 | 41 | const SECP_GENERATOR_Y: Fp = Fp::from_raw([ 42 | 0xCBB6406837BF51F5, 43 | 0x2BCE33576B315ECE, 44 | 0x8EE7EB4A7C0F9E16, 45 | 0x4FE342E2FE1A7F9B, 46 | ]); 47 | 48 | const SECP_A: Fp = Fp::from_raw([ 49 | 0xFFFFFFFFFFFFFFFC, 50 | 0x00000000FFFFFFFF, 51 | 0x0000000000000000, 52 | 0xFFFFFFFF00000001, 53 | ]); 54 | const SECP_B: Fp = Fp::from_raw([ 55 | 0x3BCE3C3E27D2604B, 56 | 0x651D06B0CC53B0F6, 57 | 0xB3EBBD55769886BC, 58 | 0x5AC635D8AA3A93E7, 59 | ]); 60 | 61 | use crate::{ 62 | batch_add, impl_add_binop_specify_output, impl_binops_additive, 63 | impl_binops_additive_specify_output, impl_binops_multiplicative, 64 | impl_binops_multiplicative_mixed, impl_sub_binop_specify_output, new_curve_impl, 65 | }; 66 | 67 | new_curve_impl!( 68 | (pub), 69 | Secp256r1, 70 | Secp256r1Affine, 71 | true, 72 | Fp, 73 | Fq, 74 | (SECP_GENERATOR_X,SECP_GENERATOR_Y), 75 | SECP_A, 76 | SECP_B, 77 | "secp256r1", 78 | |_, _| unimplemented!(), 79 | ); 80 | 81 | impl CurveAffineExt for Secp256r1Affine { 82 | batch_add!(); 83 | 84 | fn into_coordinates(self) -> (Self::Base, Self::Base) { 85 | (self.x, self.y) 86 | } 87 | } 88 | 89 | #[test] 90 | fn test_curve() { 91 | crate::tests::curve::curve_tests::(); 92 | } 93 | 94 | #[test] 95 | fn test_serialization() { 96 | crate::tests::curve::random_serialization_test::(); 97 | #[cfg(feature = "derive_serde")] 98 | crate::tests::curve::random_serde_test::(); 99 | } 100 | 101 | #[test] 102 | fn ecdsa_example() { 103 | use crate::group::Curve; 104 | use crate::CurveAffine; 105 | use ff::FromUniformBytes; 106 | use rand_core::OsRng; 107 | 108 | fn mod_n(x: Fp) -> Fq { 109 | let mut x_repr = [0u8; 32]; 110 | x_repr.copy_from_slice(x.to_repr().as_ref()); 111 | let mut x_bytes = [0u8; 64]; 112 | x_bytes[..32].copy_from_slice(&x_repr[..]); 113 | Fq::from_uniform_bytes(&x_bytes) 114 | } 115 | 116 | let g = Secp256r1::generator(); 117 | 118 | for _ in 0..1000 { 119 | // Generate a key pair 120 | let sk = Fq::random(OsRng); 121 | let pk = (g * sk).to_affine(); 122 | 123 | // Generate a valid signature 124 | // Suppose `m_hash` is the message hash 125 | let msg_hash = Fq::random(OsRng); 126 | 127 | let (r, s) = { 128 | // Draw arandomness 129 | let k = Fq::random(OsRng); 130 | let k_inv = k.invert().unwrap(); 131 | 132 | // Calculate `r` 133 | let r_point = (g * k).to_affine().coordinates().unwrap(); 134 | let x = r_point.x(); 135 | let r = mod_n(*x); 136 | 137 | // Calculate `s` 138 | let s = k_inv * (msg_hash + (r * sk)); 139 | 140 | (r, s) 141 | }; 142 | 143 | { 144 | // Verify 145 | let s_inv = s.invert().unwrap(); 146 | let u_1 = msg_hash * s_inv; 147 | let u_2 = r * s_inv; 148 | 149 | let v_1 = g * u_1; 150 | let v_2 = pk * u_2; 151 | 152 | let r_point = (v_1 + v_2).to_affine().coordinates().unwrap(); 153 | let x_candidate = r_point.x(); 154 | let r_candidate = mod_n(*x_candidate); 155 | 156 | assert_eq!(r, r_candidate); 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/secp256r1/fp.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::{adc, mac, sbb}; 2 | use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; 3 | use crate::{ 4 | field_arithmetic, field_bits, field_common, field_specific, impl_add_binop_specify_output, 5 | impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, 6 | impl_binops_multiplicative_mixed, impl_from_u64, impl_sub_binop_specify_output, impl_sum_prod, 7 | }; 8 | use core::convert::TryInto; 9 | use core::fmt; 10 | use core::ops::{Add, Mul, Neg, Sub}; 11 | use rand::RngCore; 12 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 13 | 14 | #[cfg(feature = "derive_serde")] 15 | use serde::{Deserialize, Serialize}; 16 | 17 | /// This represents an element of $\mathbb{F}_p$ where 18 | /// 19 | /// `p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff 20 | /// 21 | /// is the base field of the secp256r1 curve. 22 | // The internal representation of this type is four 64-bit unsigned 23 | // integers in little-endian order. `Fp` values are always in 24 | // Montgomery form; i.e., Fp(a) = aR mod p, with R = 2^256. 25 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 26 | #[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] 27 | pub struct Fp(pub(crate) [u64; 4]); 28 | 29 | /// Constant representing the modulus 30 | /// p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff 31 | const MODULUS: Fp = Fp([ 32 | 0xffffffffffffffff, 33 | 0x00000000ffffffff, 34 | 0x0000000000000000, 35 | 0xffffffff00000001, 36 | ]); 37 | 38 | /// Constant representing the multiplicative generator of the modulus. 39 | /// It's derived with SageMath with: `GF(MODULUS).primitive_element()`. 40 | const MULTIPLICATIVE_GENERATOR: Fp = Fp::from_raw([0x06, 0x00, 0x00, 0x00]); 41 | 42 | /// The modulus as u32 limbs. 43 | #[cfg(not(target_pointer_width = "64"))] 44 | const MODULUS_LIMBS_32: [u32; 8] = [ 45 | 0xffff_ffff, 46 | 0xffff_ffff, 47 | 0xffff_ffff, 48 | 0x0000_0000, 49 | 0x0000_0000, 50 | 0x0000_0000, 51 | 0x0000_0001, 52 | 0xffff_ffff, 53 | ]; 54 | 55 | /// Constant representing the modolus as static str 56 | const MODULUS_STR: &str = "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff"; 57 | 58 | /// INV = -(p^{-1} mod 2^64) mod 2^64 59 | const INV: u64 = 0x1; 60 | 61 | /// R = 2^256 mod p 62 | /// 0xfffffffeffffffffffffffffffffffff000000000000000000000001 63 | const R: Fp = Fp([ 64 | 0x0000000000000001, 65 | 0xffffffff00000000, 66 | 0xffffffffffffffff, 67 | 0xfffffffe, 68 | ]); 69 | 70 | /// R^2 = 2^512 mod p 71 | /// 0x4fffffffdfffffffffffffffefffffffbffffffff0000000000000003 72 | const R2: Fp = Fp([ 73 | 0x0000000000000003, 74 | 0xfffffffbffffffff, 75 | 0xfffffffffffffffe, 76 | 0x4fffffffd, 77 | ]); 78 | 79 | /// R^3 = 2^768 mod p 80 | /// 0x180000000100000005fffffffcffffffedfffffff7fffffffd0000000a 81 | const R3: Fp = Fp([ 82 | 0xfffffffd0000000a, 83 | 0xffffffedfffffff7, 84 | 0x00000005fffffffc, 85 | 0x1800000001, 86 | ]); 87 | 88 | /// 1 / 2 mod p 89 | /// 0x7fffffff80000000800000000000000000000000800000000000000000000000 90 | const TWO_INV: Fp = Fp::from_raw([ 91 | 0x0000000000000000, 92 | 0x0000000080000000, 93 | 0x8000000000000000, 94 | 0x7fffffff80000000, 95 | ]); 96 | 97 | const ZETA: Fp = Fp::from_raw([ 98 | 0xd964598eb819acce, 99 | 0x2e68c59bdef3e53f, 100 | 0x62388a8e0ef62331, 101 | 0x4d6ea8928adb86cf, 102 | ]); 103 | 104 | /// Generator of the t-order multiplicative subgroup. 105 | /// Computed by exponentiating Self::MULTIPLICATIVE_GENERATOR by 2^s, where s is Self::S. 106 | /// `0x0000000000000000000000000000000000000000000000000000000000000024`. 107 | const DELTA: Fp = Fp::from_raw([0x24, 0, 0, 0]); 108 | 109 | /// Implementations of this trait MUST ensure that this is the generator used to derive Self::ROOT_OF_UNITY. 110 | /// Derived from: 111 | /// ```ignore 112 | /// Zp(Zp(mul_generator)^t) where t = (modulus - 1 )/ 2 113 | /// 115792089237316195423570985008687907853269984665640564039457584007908834671662 114 | /// ``` 115 | /// `0xffffffff00000001000000000000000000000000fffffffffffffffffffffffe` 116 | const ROOT_OF_UNITY: Fp = Fp::from_raw([ 117 | 0xfffffffffffffffe, 118 | 0x00000000ffffffff, 119 | 0x0000000000000000, 120 | 0xffffffff00000001, 121 | ]); 122 | 123 | /// Inverse of [`ROOT_OF_UNITY`]. 124 | /// `0xffffffff00000001000000000000000000000000fffffffffffffffffffffffe` 125 | const ROOT_OF_UNITY_INV: Fp = Fp::from_raw([ 126 | 0xfffffffffffffffe, 127 | 0x00000000ffffffff, 128 | 0x0000000000000000, 129 | 0xffffffff00000001, 130 | ]); 131 | 132 | impl_binops_additive!(Fp, Fp); 133 | impl_binops_multiplicative!(Fp, Fp); 134 | field_common!( 135 | Fp, 136 | MODULUS, 137 | INV, 138 | MODULUS_STR, 139 | TWO_INV, 140 | ROOT_OF_UNITY_INV, 141 | DELTA, 142 | ZETA, 143 | R, 144 | R2, 145 | R3 146 | ); 147 | impl_from_u64!(Fp, R2); 148 | field_arithmetic!(Fp, MODULUS, INV, dense); 149 | impl_sum_prod!(Fp); 150 | 151 | #[cfg(target_pointer_width = "64")] 152 | field_bits!(Fp, MODULUS); 153 | #[cfg(not(target_pointer_width = "64"))] 154 | field_bits!(Fp, MODULUS, MODULUS_LIMBS_32); 155 | 156 | impl Fp { 157 | pub const fn size() -> usize { 158 | 32 159 | } 160 | } 161 | 162 | impl ff::Field for Fp { 163 | const ZERO: Self = Self::zero(); 164 | const ONE: Self = Self::one(); 165 | 166 | fn random(mut rng: impl RngCore) -> Self { 167 | Self::from_u512([ 168 | rng.next_u64(), 169 | rng.next_u64(), 170 | rng.next_u64(), 171 | rng.next_u64(), 172 | rng.next_u64(), 173 | rng.next_u64(), 174 | rng.next_u64(), 175 | rng.next_u64(), 176 | ]) 177 | } 178 | 179 | fn double(&self) -> Self { 180 | self.double() 181 | } 182 | 183 | #[inline(always)] 184 | fn square(&self) -> Self { 185 | self.square() 186 | } 187 | 188 | /// Computes the square root of this element, if it exists. 189 | fn sqrt(&self) -> CtOption { 190 | let tmp = self.pow([ 191 | 0x0000000000000000, 192 | 0x0000000040000000, 193 | 0x4000000000000000, 194 | 0x3fffffffc0000000, 195 | ]); 196 | 197 | CtOption::new(tmp, tmp.square().ct_eq(self)) 198 | } 199 | 200 | /// Returns the multiplicative inverse of the 201 | /// element. If it is zero, the method fails. 202 | fn invert(&self) -> CtOption { 203 | self.invert() 204 | } 205 | 206 | fn pow_vartime>(&self, exp: S) -> Self { 207 | let mut res = Self::one(); 208 | let mut found_one = false; 209 | for e in exp.as_ref().iter().rev() { 210 | for i in (0..64).rev() { 211 | if found_one { 212 | res = res.square(); 213 | } 214 | 215 | if ((*e >> i) & 1) == 1 { 216 | found_one = true; 217 | res *= self; 218 | } 219 | } 220 | } 221 | res 222 | } 223 | 224 | fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { 225 | ff::helpers::sqrt_ratio_generic(num, div) 226 | } 227 | } 228 | 229 | impl ff::PrimeField for Fp { 230 | type Repr = [u8; 32]; 231 | 232 | const MODULUS: &'static str = MODULUS_STR; 233 | const MULTIPLICATIVE_GENERATOR: Self = MULTIPLICATIVE_GENERATOR; 234 | const TWO_INV: Self = TWO_INV; 235 | const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; 236 | const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; 237 | const DELTA: Self = DELTA; 238 | const NUM_BITS: u32 = 256; 239 | const CAPACITY: u32 = 255; 240 | const S: u32 = 1; 241 | 242 | fn from_repr(repr: Self::Repr) -> CtOption { 243 | let mut tmp = Fp([0, 0, 0, 0]); 244 | 245 | tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); 246 | tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); 247 | tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); 248 | tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); 249 | 250 | // Try to subtract the modulus 251 | let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); 252 | let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); 253 | let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); 254 | let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); 255 | 256 | // If the element is smaller than MODULUS then the 257 | // subtraction will underflow, producing a borrow value 258 | // of 0xffff...ffff. Otherwise, it'll be zero. 259 | let is_some = (borrow as u8) & 1; 260 | 261 | // Convert to Montgomery form by computing 262 | // (a.R^0 * R^2) / R = a.R 263 | tmp *= &R2; 264 | 265 | CtOption::new(tmp, Choice::from(is_some)) 266 | } 267 | 268 | fn to_repr(&self) -> Self::Repr { 269 | // Turn into canonical form by computing 270 | // (a.R) / R = a 271 | let tmp = Fp::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); 272 | 273 | let mut res = [0; 32]; 274 | res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); 275 | res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); 276 | res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); 277 | res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); 278 | 279 | res 280 | } 281 | 282 | fn from_u128(v: u128) -> Self { 283 | Self::from_raw([v as u64, (v >> 64) as u64, 0, 0]) 284 | } 285 | 286 | fn is_odd(&self) -> Choice { 287 | Choice::from(self.to_repr()[0] & 1) 288 | } 289 | } 290 | 291 | impl FromUniformBytes<64> for Fp { 292 | /// Converts a 512-bit little endian integer into 293 | /// an `Fp` by reducing by the modulus. 294 | fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { 295 | Self::from_u512([ 296 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 297 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 298 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 299 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 300 | u64::from_le_bytes(bytes[32..40].try_into().unwrap()), 301 | u64::from_le_bytes(bytes[40..48].try_into().unwrap()), 302 | u64::from_le_bytes(bytes[48..56].try_into().unwrap()), 303 | u64::from_le_bytes(bytes[56..64].try_into().unwrap()), 304 | ]) 305 | } 306 | } 307 | 308 | impl WithSmallOrderMulGroup<3> for Fp { 309 | const ZETA: Self = ZETA; 310 | } 311 | 312 | #[cfg(test)] 313 | mod test { 314 | use super::*; 315 | use ff::Field; 316 | use rand_core::OsRng; 317 | 318 | #[test] 319 | fn test_sqrt() { 320 | // NB: TWO_INV is standing in as a "random" field element 321 | let v = (Fp::TWO_INV).square().sqrt().unwrap(); 322 | assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV); 323 | 324 | for _ in 0..10000 { 325 | let a = Fp::random(OsRng); 326 | let mut b = a; 327 | b = b.square(); 328 | 329 | let b = b.sqrt().unwrap(); 330 | let mut negb = b; 331 | negb = negb.neg(); 332 | 333 | assert!(a == b || a == negb); 334 | } 335 | } 336 | 337 | #[test] 338 | fn test_constants() { 339 | assert_eq!( 340 | Fp::MODULUS, 341 | "0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff", 342 | ); 343 | 344 | assert_eq!(Fp::from(2) * Fp::TWO_INV, Fp::ONE); 345 | } 346 | 347 | #[test] 348 | fn test_delta() { 349 | assert_eq!(Fp::DELTA, MULTIPLICATIVE_GENERATOR.pow([1u64 << Fp::S])); 350 | } 351 | 352 | #[test] 353 | fn test_root_of_unity() { 354 | assert_eq!(Fp::ROOT_OF_UNITY.pow_vartime([1 << Fp::S]), Fp::one()); 355 | } 356 | 357 | #[test] 358 | fn test_inv_root_of_unity() { 359 | assert_eq!(Fp::ROOT_OF_UNITY_INV, Fp::ROOT_OF_UNITY.invert().unwrap()); 360 | } 361 | 362 | #[test] 363 | fn test_field() { 364 | crate::tests::field::random_field_tests::("secp256k1 base".to_string()); 365 | } 366 | 367 | #[test] 368 | #[cfg(feature = "bits")] 369 | fn test_bits() { 370 | crate::tests::field::random_bits_tests::("secp256k1 base".to_string()); 371 | } 372 | 373 | #[test] 374 | fn test_serialization() { 375 | crate::tests::field::random_serialization_test::("secp256k1 base".to_string()); 376 | #[cfg(feature = "derive_serde")] 377 | crate::tests::field::random_serde_test::("secp256k1 base".to_string()); 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/secp256r1/fq.rs: -------------------------------------------------------------------------------- 1 | use crate::arithmetic::{adc, mac, sbb}; 2 | use crate::ff::{FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; 3 | use core::convert::TryInto; 4 | use core::fmt; 5 | use core::ops::{Add, Mul, Neg, Sub}; 6 | use rand::RngCore; 7 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 8 | 9 | #[cfg(feature = "derive_serde")] 10 | use serde::{Deserialize, Serialize}; 11 | 12 | /// This represents an element of $\mathbb{F}_q$ where 13 | /// 14 | /// `q = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551` 15 | /// 16 | /// is the scalar field of the secp256r1 curve. 17 | // The internal representation of this type is four 64-bit unsigned 18 | // integers in little-endian order. `Fq` values are always in 19 | // Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256. 20 | #[derive(Clone, Copy, PartialEq, Eq, Hash)] 21 | #[cfg_attr(feature = "derive_serde", derive(Serialize, Deserialize))] 22 | pub struct Fq(pub(crate) [u64; 4]); 23 | 24 | /// Constant representing the modulus 25 | /// q = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 26 | const MODULUS: Fq = Fq([ 27 | 0xf3b9cac2fc632551, 28 | 0xbce6faada7179e84, 29 | 0xffffffffffffffff, 30 | 0xffffffff00000000, 31 | ]); 32 | 33 | /// The modulus as u32 limbs. 34 | #[cfg(not(target_pointer_width = "64"))] 35 | const MODULUS_LIMBS_32: [u32; 8] = [ 36 | 0xfc63_2551, 37 | 0xf3b9_cac2, 38 | 0xa717_9e84, 39 | 0xbce6_faad, 40 | 0xffff_ffff, 41 | 0xffff_ffff, 42 | 0x0000_0000, 43 | 0xffff_ffff, 44 | ]; 45 | 46 | ///Constant representing the modulus as static str 47 | const MODULUS_STR: &str = "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"; 48 | 49 | /// INV = -(q^{-1} mod 2^64) mod 2^64 50 | const INV: u64 = 0xccd1c8aaee00bc4f; 51 | 52 | /// R = 2^256 mod q 53 | /// 0xffffffff00000000000000004319055258e8617b0c46353d039cdaaf 54 | const R: Fq = Fq([ 55 | 0x0c46353d039cdaaf, 56 | 0x4319055258e8617b, 57 | 0x0000000000000000, 58 | 0xffffffff, 59 | ]); 60 | 61 | /// R^2 = 2^512 mod q 62 | /// 0x66e12d94f3d956202845b2392b6bec594699799c49bd6fa683244c95be79eea2 63 | const R2: Fq = Fq([ 64 | 0x83244c95be79eea2, 65 | 0x4699799c49bd6fa6, 66 | 0x2845b2392b6bec59, 67 | 0x66e12d94f3d95620, 68 | ]); 69 | 70 | /// R^3 = 2^768 mod q 71 | /// 0x503a54e76407be652543b9246ba5e93f111f28ae0c0555c9ac8ebec90b65a624 72 | const R3: Fq = Fq([ 73 | 0xac8ebec90b65a624, 74 | 0x111f28ae0c0555c9, 75 | 0x2543b9246ba5e93f, 76 | 0x503a54e76407be65, 77 | ]); 78 | 79 | /// `GENERATOR = 7 mod r` is a generator of the `q - 1` order multiplicative 80 | /// subgroup, or in other words a primitive root of the field. 81 | /// It's derived with SageMath with: `GF(MODULUS).primitive_element()`. 82 | const GENERATOR: Fq = Fq::from_raw([0x07, 0x00, 0x00, 0x00]); 83 | 84 | /// GENERATOR^t where t * 2^s + 1 = r with t odd. In other words, this is a 2^s root of unity. 85 | /// `ffc97f062a770992ba807ace842a3dfc1546cad004378daf0592d7fbb41e6602` 86 | const ROOT_OF_UNITY: Fq = Fq::from_raw([ 87 | 0x0592d7fbb41e6602, 88 | 0x1546cad004378daf, 89 | 0xba807ace842a3dfc, 90 | 0xffc97f062a770992, 91 | ]); 92 | 93 | /// 1 / ROOT_OF_UNITY mod q 94 | /// `a0a66a5562d46f2ac645fa0458131caee3ac117c794c4137379c7f0657c73764` 95 | const ROOT_OF_UNITY_INV: Fq = Fq::from_raw([ 96 | 0x379c7f0657c73764, 97 | 0xe3ac117c794c4137, 98 | 0xc645fa0458131cae, 99 | 0xa0a66a5562d46f2a, 100 | ]); 101 | 102 | /// 1 / 2 mod q 103 | const TWO_INV: Fq = Fq::from_raw([ 104 | 0x79dce5617e3192a9, 105 | 0xde737d56d38bcf42, 106 | 0x7fffffffffffffff, 107 | 0x7fffffff80000000, 108 | ]); 109 | 110 | const ZETA: Fq = Fq::from_raw([ 111 | 0x7cbf87ff12884e21, 112 | 0x9405335ce9c83e1d, 113 | 0x4e786d0777fd6aef, 114 | 0x52891d43d946a035, 115 | ]); 116 | 117 | /// Generator of the t-order multiplicative subgroup. 118 | /// Computed by exponentiating Self::MULTIPLICATIVE_GENERATOR by 2^s, where s is Self::S. 119 | const DELTA: Fq = Fq::from_raw([0x1e39a5057d81, 0, 0, 0]); 120 | 121 | use crate::{ 122 | field_arithmetic, field_common, field_specific, impl_add_binop_specify_output, 123 | impl_binops_additive, impl_binops_additive_specify_output, impl_binops_multiplicative, 124 | impl_binops_multiplicative_mixed, impl_from_u64, impl_sub_binop_specify_output, impl_sum_prod, 125 | }; 126 | impl_binops_additive!(Fq, Fq); 127 | impl_binops_multiplicative!(Fq, Fq); 128 | field_common!( 129 | Fq, 130 | MODULUS, 131 | INV, 132 | MODULUS_STR, 133 | TWO_INV, 134 | ROOT_OF_UNITY_INV, 135 | DELTA, 136 | ZETA, 137 | R, 138 | R2, 139 | R3 140 | ); 141 | impl_from_u64!(Fq, R2); 142 | field_arithmetic!(Fq, MODULUS, INV, dense); 143 | impl_sum_prod!(Fq); 144 | 145 | impl Fq { 146 | pub const fn size() -> usize { 147 | 32 148 | } 149 | } 150 | 151 | impl ff::Field for Fq { 152 | const ZERO: Self = Self::zero(); 153 | const ONE: Self = Self::one(); 154 | 155 | fn random(mut rng: impl RngCore) -> Self { 156 | Self::from_u512([ 157 | rng.next_u64(), 158 | rng.next_u64(), 159 | rng.next_u64(), 160 | rng.next_u64(), 161 | rng.next_u64(), 162 | rng.next_u64(), 163 | rng.next_u64(), 164 | rng.next_u64(), 165 | ]) 166 | } 167 | 168 | fn double(&self) -> Self { 169 | self.double() 170 | } 171 | 172 | #[inline(always)] 173 | fn square(&self) -> Self { 174 | self.square() 175 | } 176 | 177 | /// Returns the multiplicative inverse of the 178 | /// element. If it is zero, the method fails. 179 | fn invert(&self) -> CtOption { 180 | self.invert() 181 | } 182 | 183 | fn pow_vartime>(&self, exp: S) -> Self { 184 | let mut res = Self::one(); 185 | let mut found_one = false; 186 | for e in exp.as_ref().iter().rev() { 187 | for i in (0..64).rev() { 188 | if found_one { 189 | res = res.square(); 190 | } 191 | 192 | if ((*e >> i) & 1) == 1 { 193 | found_one = true; 194 | res *= self; 195 | } 196 | } 197 | } 198 | res 199 | } 200 | 201 | fn sqrt(&self) -> CtOption { 202 | // 7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a 203 | let tm1d2 = [ 204 | 0x279dce5617e3192a, 205 | 0xfde737d56d38bcf4, 206 | 0x07ffffffffffffff, 207 | 0x7fffffff8000000, 208 | ]; 209 | 210 | ff::helpers::sqrt_tonelli_shanks(self, tm1d2) 211 | } 212 | 213 | fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { 214 | ff::helpers::sqrt_ratio_generic(num, div) 215 | } 216 | } 217 | 218 | impl ff::PrimeField for Fq { 219 | type Repr = [u8; 32]; 220 | 221 | const NUM_BITS: u32 = 256; 222 | const CAPACITY: u32 = 255; 223 | const MODULUS: &'static str = MODULUS_STR; 224 | const MULTIPLICATIVE_GENERATOR: Self = GENERATOR; 225 | const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; 226 | const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; 227 | const TWO_INV: Self = TWO_INV; 228 | const DELTA: Self = DELTA; 229 | const S: u32 = 4; 230 | 231 | fn from_repr(repr: Self::Repr) -> CtOption { 232 | let mut tmp = Fq([0, 0, 0, 0]); 233 | 234 | tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); 235 | tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); 236 | tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); 237 | tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); 238 | 239 | // Try to subtract the modulus 240 | let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); 241 | let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); 242 | let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); 243 | let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); 244 | 245 | // If the element is smaller than MODULUS then the 246 | // subtraction will underflow, producing a borrow value 247 | // of 0xffff...ffff. Otherwise, it'll be zero. 248 | let is_some = (borrow as u8) & 1; 249 | 250 | // Convert to Montgomery form by computi 251 | // (a.R^0 * R^2) / R = a.R 252 | tmp *= &R2; 253 | 254 | CtOption::new(tmp, Choice::from(is_some)) 255 | } 256 | 257 | fn to_repr(&self) -> Self::Repr { 258 | // Turn into canonical form by computing 259 | // (a.R) / R = a 260 | let tmp = Fq::montgomery_reduce(&[self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0]); 261 | 262 | let mut res = [0; 32]; 263 | res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); 264 | res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); 265 | res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); 266 | res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); 267 | 268 | res 269 | } 270 | 271 | fn is_odd(&self) -> Choice { 272 | Choice::from(self.to_repr()[0] & 1) 273 | } 274 | } 275 | 276 | impl FromUniformBytes<64> for Fq { 277 | /// Converts a 512-bit little endian integer into 278 | /// an `Fq` by reducing by the modulus. 279 | fn from_uniform_bytes(bytes: &[u8; 64]) -> Self { 280 | Self::from_u512([ 281 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 282 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 283 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 284 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 285 | u64::from_le_bytes(bytes[32..40].try_into().unwrap()), 286 | u64::from_le_bytes(bytes[40..48].try_into().unwrap()), 287 | u64::from_le_bytes(bytes[48..56].try_into().unwrap()), 288 | u64::from_le_bytes(bytes[56..64].try_into().unwrap()), 289 | ]) 290 | } 291 | } 292 | 293 | impl WithSmallOrderMulGroup<3> for Fq { 294 | const ZETA: Self = ZETA; 295 | } 296 | 297 | #[cfg(test)] 298 | mod test { 299 | use super::*; 300 | use ff::Field; 301 | use rand_core::OsRng; 302 | 303 | #[test] 304 | fn test_zeta() { 305 | assert_eq!(Fq::ZETA * Fq::ZETA * Fq::ZETA, Fq::ONE); 306 | assert_ne!(Fq::ZETA * Fq::ZETA, Fq::ONE); 307 | } 308 | 309 | #[test] 310 | fn test_sqrt() { 311 | // NB: TWO_INV is standing in as a "random" field element 312 | // let v = (Fq::TWO_INV).square().sqrt().unwrap(); 313 | // assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV); 314 | 315 | for _ in 0..10000 { 316 | let a = Fq::random(OsRng); 317 | let mut b = a; 318 | b = b.square(); 319 | 320 | let b = b.sqrt().unwrap(); 321 | let mut negb = b; 322 | negb = negb.neg(); 323 | 324 | assert!(a == b || a == negb); 325 | } 326 | } 327 | 328 | #[test] 329 | fn test_constants() { 330 | assert_eq!( 331 | Fq::MODULUS, 332 | "0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551", 333 | ); 334 | 335 | assert_eq!(Fq::from(2) * Fq::TWO_INV, Fq::ONE); 336 | } 337 | 338 | #[test] 339 | fn test_delta() { 340 | assert_eq!(Fq::DELTA, Fq::MULTIPLICATIVE_GENERATOR.pow([1u64 << Fq::S])); 341 | } 342 | 343 | #[test] 344 | fn test_root_of_unity() { 345 | assert_eq!(Fq::ROOT_OF_UNITY.pow_vartime([1 << Fq::S]), Fq::one()); 346 | } 347 | 348 | #[test] 349 | fn test_inv_root_of_unity() { 350 | assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::ROOT_OF_UNITY.invert().unwrap()); 351 | } 352 | 353 | #[test] 354 | fn test_field() { 355 | crate::tests::field::random_field_tests::("secp256r1 scalar".to_string()); 356 | } 357 | 358 | #[test] 359 | fn test_serialization() { 360 | crate::tests::field::random_serialization_test::("secp256r1 scalar".to_string()); 361 | #[cfg(feature = "derive_serde")] 362 | crate::tests::field::random_serde_test::("secp256r1 scalar".to_string()); 363 | } 364 | } 365 | -------------------------------------------------------------------------------- /src/secp256r1/mod.rs: -------------------------------------------------------------------------------- 1 | mod curve; 2 | mod fp; 3 | mod fq; 4 | 5 | pub use curve::*; 6 | pub use fp::*; 7 | pub use fq::*; 8 | -------------------------------------------------------------------------------- /src/serde.rs: -------------------------------------------------------------------------------- 1 | use std::io::{self, Read, Write}; 2 | 3 | /// Trait for converting raw bytes to/from the internal representation of a type. 4 | /// For example, field elements are represented in Montgomery form and serialized/deserialized without Montgomery reduction. 5 | pub trait SerdeObject: Sized { 6 | /// The purpose of unchecked functions is to read the internal memory representation 7 | /// of a type from bytes as quickly as possible. No sanitization checks are performed 8 | /// to ensure the bytes represent a valid object. As such this function should only be 9 | /// used internally as an extension of machine memory. It should not be used to deserialize 10 | /// externally provided data. 11 | fn from_raw_bytes_unchecked(bytes: &[u8]) -> Self; 12 | fn from_raw_bytes(bytes: &[u8]) -> Option; 13 | 14 | fn to_raw_bytes(&self) -> Vec; 15 | 16 | /// The purpose of unchecked functions is to read the internal memory representation 17 | /// of a type from disk as quickly as possible. No sanitization checks are performed 18 | /// to ensure the bytes represent a valid object. This function should only be used 19 | /// internally when some machine state cannot be kept in memory (e.g., between runs) 20 | /// and needs to be reloaded as quickly as possible. 21 | fn read_raw_unchecked(reader: &mut R) -> Self; 22 | fn read_raw(reader: &mut R) -> io::Result; 23 | 24 | fn write_raw(&self, writer: &mut W) -> io::Result<()>; 25 | } 26 | -------------------------------------------------------------------------------- /src/tests/curve.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::eq_op)] 2 | 3 | use crate::ff::Field; 4 | use crate::group::prime::PrimeCurveAffine; 5 | use crate::tests::fe_from_str; 6 | use crate::{group::GroupEncoding, serde::SerdeObject}; 7 | use crate::{hash_to_curve, CurveAffine, CurveExt}; 8 | use rand_core::{OsRng, RngCore}; 9 | use std::iter; 10 | 11 | #[cfg(feature = "derive_serde")] 12 | use serde::{Deserialize, Serialize}; 13 | 14 | pub fn curve_tests() { 15 | is_on_curve::(); 16 | equality::(); 17 | projective_to_affine_affine_to_projective::(); 18 | projective_addition::(); 19 | mixed_addition::(); 20 | multiplication::(); 21 | batch_normalize::(); 22 | serdes::(); 23 | } 24 | 25 | fn serdes() { 26 | assert!(bool::from( 27 | G::from_bytes(&G::identity().to_bytes()) 28 | .unwrap() 29 | .is_identity() 30 | )); 31 | assert!(bool::from( 32 | G::AffineExt::from_bytes(&G::AffineExt::identity().to_bytes()) 33 | .unwrap() 34 | .is_identity() 35 | )); 36 | for _ in 0..100 { 37 | let projective_point = G::random(OsRng); 38 | let affine_point: G::AffineExt = projective_point.into(); 39 | let projective_repr = projective_point.to_bytes(); 40 | let affine_repr = affine_point.to_bytes(); 41 | 42 | println!( 43 | "{:?} \n{:?}", 44 | projective_repr.as_ref(), 45 | affine_repr.as_ref() 46 | ); 47 | 48 | let projective_point_rec = G::from_bytes(&projective_repr).unwrap(); 49 | let projective_point_rec_unchecked = G::from_bytes(&projective_repr).unwrap(); 50 | let affine_point_rec = G::AffineExt::from_bytes(&affine_repr).unwrap(); 51 | let affine_point_rec_unchecked = G::AffineExt::from_bytes(&affine_repr).unwrap(); 52 | 53 | assert_eq!(projective_point, projective_point_rec); 54 | assert_eq!(projective_point, projective_point_rec_unchecked); 55 | assert_eq!(affine_point, affine_point_rec); 56 | assert_eq!(affine_point, affine_point_rec_unchecked); 57 | } 58 | } 59 | 60 | #[cfg(feature = "derive_serde")] 61 | pub fn random_serde_test() 62 | where 63 | G: SerdeObject + CurveExt + Serialize + for<'de> Deserialize<'de>, 64 | G::AffineExt: SerdeObject + Serialize + for<'de> Deserialize<'de>, 65 | { 66 | for _ in 0..100 { 67 | let projective_point = G::random(OsRng); 68 | let affine_point: G::AffineExt = projective_point.into(); 69 | { 70 | let affine_bytes = bincode::serialize(&affine_point).unwrap(); 71 | let reader = std::io::Cursor::new(affine_bytes); 72 | let affine_point_rec: G::AffineExt = bincode::deserialize_from(reader).unwrap(); 73 | assert_eq!(projective_point.to_affine(), affine_point_rec); 74 | assert_eq!(affine_point, affine_point_rec); 75 | } 76 | { 77 | let projective_bytes = bincode::serialize(&projective_point).unwrap(); 78 | let reader = std::io::Cursor::new(projective_bytes); 79 | let projective_point_rec: G = bincode::deserialize_from(reader).unwrap(); 80 | assert_eq!(projective_point, projective_point_rec); 81 | } 82 | } 83 | } 84 | 85 | pub fn random_serialization_test() 86 | where 87 | G: SerdeObject, 88 | G::AffineExt: SerdeObject, 89 | { 90 | for _ in 0..100 { 91 | let projective_point = G::random(OsRng); 92 | let affine_point: G::AffineExt = projective_point.into(); 93 | 94 | let projective_bytes = projective_point.to_raw_bytes(); 95 | let projective_point_rec = G::from_raw_bytes(&projective_bytes).unwrap(); 96 | assert_eq!(projective_point, projective_point_rec); 97 | let mut buf = Vec::new(); 98 | projective_point.write_raw(&mut buf).unwrap(); 99 | let projective_point_rec = G::read_raw(&mut &buf[..]).unwrap(); 100 | assert_eq!(projective_point, projective_point_rec); 101 | 102 | let affine_bytes = affine_point.to_raw_bytes(); 103 | let affine_point_rec = G::AffineExt::from_raw_bytes(&affine_bytes).unwrap(); 104 | assert_eq!(affine_point, affine_point_rec); 105 | let mut buf = Vec::new(); 106 | affine_point.write_raw(&mut buf).unwrap(); 107 | let affine_point_rec = G::AffineExt::read_raw(&mut &buf[..]).unwrap(); 108 | assert_eq!(affine_point, affine_point_rec); 109 | } 110 | } 111 | 112 | fn is_on_curve() { 113 | assert!(bool::from(G::identity().is_on_curve())); 114 | assert!(bool::from(G::generator().is_on_curve())); 115 | assert!(bool::from(G::identity().is_on_curve())); 116 | assert!(bool::from(G::generator().is_on_curve())); 117 | 118 | for _ in 0..100 { 119 | let point = G::random(OsRng); 120 | assert!(bool::from(point.is_on_curve())); 121 | let affine_point: G::AffineExt = point.into(); 122 | assert!(bool::from(affine_point.is_on_curve())); 123 | } 124 | } 125 | 126 | fn equality() { 127 | let a = G::generator(); 128 | let b = G::identity(); 129 | 130 | assert!(a == a); 131 | assert!(b == b); 132 | assert!(a != b); 133 | assert!(b != a); 134 | 135 | for _ in 0..100 { 136 | let a = G::random(OsRng); 137 | let b = G::random(OsRng); 138 | 139 | assert!(a == a); 140 | assert!(b == b); 141 | assert!(a != b); 142 | assert!(b != a); 143 | 144 | let a: G::AffineExt = a.into(); 145 | let b: G::AffineExt = b.into(); 146 | 147 | assert!(a == a); 148 | assert!(b == b); 149 | assert!(a != b); 150 | assert!(b != a); 151 | } 152 | } 153 | 154 | fn projective_to_affine_affine_to_projective() { 155 | let a = G::generator(); 156 | let b = G::identity(); 157 | 158 | assert!(bool::from(G::AffineExt::from(a).is_on_curve())); 159 | assert!(!bool::from(G::AffineExt::from(a).is_identity())); 160 | assert!(bool::from(G::AffineExt::from(b).is_on_curve())); 161 | assert!(bool::from(G::AffineExt::from(b).is_identity())); 162 | 163 | let a = G::AffineExt::generator(); 164 | let b = G::AffineExt::identity(); 165 | 166 | assert!(bool::from(G::from(a).is_on_curve())); 167 | assert!(!bool::from(G::from(a).is_identity())); 168 | assert!(bool::from(G::from(b).is_on_curve())); 169 | assert!(bool::from(G::from(b).is_identity())); 170 | } 171 | 172 | fn projective_addition() { 173 | let a = G::identity(); 174 | let b = G::identity(); 175 | let c = a + b; 176 | assert!(bool::from(c.is_identity())); 177 | assert!(bool::from(c.is_on_curve())); 178 | let c = a - b; 179 | assert!(bool::from(c.is_identity())); 180 | assert!(bool::from(c.is_on_curve())); 181 | 182 | let a = G::identity(); 183 | let a = -a; 184 | assert!(bool::from(a.is_on_curve())); 185 | assert!(bool::from(a.is_identity())); 186 | 187 | let a = G::random(OsRng); 188 | assert!(a == a + G::identity()); 189 | assert!(a == G::identity() + a); 190 | assert!(-a == G::identity() - a); 191 | 192 | let a = G::identity(); 193 | let a = a.double(); 194 | assert!(bool::from(c.is_on_curve())); 195 | assert!(bool::from(a.is_identity())); 196 | 197 | let a = G::generator(); 198 | let a = a.double(); 199 | assert!(bool::from(c.is_on_curve())); 200 | assert_eq!(a, G::generator() + G::generator()); 201 | 202 | let a = G::random(OsRng); 203 | assert!(a.double() - a == a); 204 | 205 | let a = G::random(OsRng); 206 | let b = G::random(OsRng); 207 | let c = G::random(OsRng); 208 | assert!(a + b == b + a); 209 | assert!(a - b == -(b - a)); 210 | assert!(c + (a + b) == a + (c + b)); 211 | assert!((a - b) - c == (a - c) - b); 212 | 213 | let a = G::generator().double().double(); // 4P 214 | let b = G::generator().double(); // 2P 215 | let c = a + b; 216 | 217 | let mut d = G::generator(); 218 | for _ in 0..5 { 219 | d += G::generator(); 220 | } 221 | 222 | assert!(c == d); 223 | assert!(!bool::from(c.is_identity())); 224 | assert!(bool::from(c.is_on_curve())); 225 | assert!(!bool::from(d.is_identity())); 226 | assert!(bool::from(d.is_on_curve())); 227 | } 228 | 229 | fn mixed_addition() { 230 | let a = G::identity(); 231 | let b = G::AffineRepr::identity(); 232 | let c = a + b; 233 | assert!(bool::from(c.is_identity())); 234 | assert!(bool::from(c.is_on_curve())); 235 | let c = a - b; 236 | assert!(bool::from(c.is_identity())); 237 | assert!(bool::from(c.is_on_curve())); 238 | 239 | let a = G::identity(); 240 | let a = -a; 241 | assert!(bool::from(a.is_on_curve())); 242 | assert!(bool::from(a.is_identity())); 243 | let a = G::AffineExt::identity(); 244 | let a = -a; 245 | assert!(bool::from(a.is_on_curve())); 246 | assert!(bool::from(a.is_identity())); 247 | 248 | let a: G::AffineExt = G::random(OsRng).into(); 249 | assert!(a.to_curve() == a + G::AffineExt::identity()); 250 | 251 | let a = G::random(OsRng); 252 | assert!(a.double() - a == a); 253 | 254 | let a = G::random(OsRng); 255 | let b: G::AffineExt = G::random(OsRng).into(); 256 | let c0 = a + b; 257 | let c1 = a + G::from(b); 258 | assert_eq!(c0, c1); 259 | } 260 | 261 | fn batch_normalize() { 262 | let a = G::generator().double(); 263 | let b = a.double(); 264 | let c = b.double(); 265 | 266 | for a_identity in (0..1).map(|n| n == 1) { 267 | for b_identity in (0..1).map(|n| n == 1) { 268 | for c_identity in (0..1).map(|n| n == 1) { 269 | let mut v = [a, b, c]; 270 | if a_identity { 271 | v[0] = G::identity() 272 | } 273 | if b_identity { 274 | v[1] = G::identity() 275 | } 276 | if c_identity { 277 | v[2] = G::identity() 278 | } 279 | 280 | let mut t = [ 281 | G::AffineExt::identity(), 282 | G::AffineExt::identity(), 283 | G::AffineExt::identity(), 284 | ]; 285 | let expected = [ 286 | G::AffineExt::from(v[0]), 287 | G::AffineExt::from(v[1]), 288 | G::AffineExt::from(v[2]), 289 | ]; 290 | 291 | G::batch_normalize(&v[..], &mut t[..]); 292 | 293 | assert_eq!(&t[..], &expected[..]); 294 | } 295 | } 296 | } 297 | } 298 | 299 | fn multiplication() { 300 | for _ in 1..1000 { 301 | let s1 = G::ScalarExt::random(OsRng); 302 | let s2 = G::ScalarExt::random(OsRng); 303 | 304 | let t0 = G::identity() * s1; 305 | assert!(bool::from(t0.is_identity())); 306 | 307 | let a = G::random(OsRng); 308 | let t0 = a * G::ScalarExt::ONE; 309 | assert_eq!(a, t0); 310 | 311 | let t0 = a * G::ScalarExt::ZERO; 312 | assert!(bool::from(t0.is_identity())); 313 | 314 | let t0 = a * s1 + a * s2; 315 | 316 | let s3 = s1 + s2; 317 | let t1 = a * s3; 318 | 319 | assert_eq!(t0, t1); 320 | 321 | let mut t0 = a * s1; 322 | let mut t1 = a * s2; 323 | t0 += t1; 324 | let s3 = s1 + s2; 325 | t1 = a * s3; 326 | assert_eq!(t0, t1); 327 | } 328 | } 329 | 330 | pub fn hash_to_curve_test() { 331 | let hasher = G::hash_to_curve("test"); 332 | let mut rng = OsRng; 333 | for _ in 0..1000 { 334 | let message = iter::repeat_with(|| rng.next_u32().to_be_bytes()) 335 | .take(32) 336 | .flatten() 337 | .collect::>(); 338 | assert!(bool::from(hasher(&message).is_on_curve())); 339 | } 340 | } 341 | 342 | pub fn svdw_map_to_curve_test( 343 | z: G::Base, 344 | precomputed_constants: [&'static str; 4], 345 | test_vector: impl IntoIterator, 346 | ) { 347 | let [c1, c2, c3, c4] = hash_to_curve::svdw_precomputed_constants::(z); 348 | assert_eq!([c1, c2, c3, c4], precomputed_constants.map(fe_from_str)); 349 | for (u, (x, y)) in test_vector.into_iter() { 350 | let u = fe_from_str(u); 351 | let expected = G::AffineExt::from_xy(fe_from_str(x), fe_from_str(y)).unwrap(); 352 | let output = hash_to_curve::svdw_map_to_curve::(u, c1, c2, c3, c4, z).to_affine(); 353 | assert_eq!(output, expected); 354 | } 355 | } 356 | -------------------------------------------------------------------------------- /src/tests/field.rs: -------------------------------------------------------------------------------- 1 | use crate::ff::Field; 2 | use crate::serde::SerdeObject; 3 | use ark_std::{end_timer, start_timer}; 4 | use rand::{RngCore, SeedableRng}; 5 | use rand_xorshift::XorShiftRng; 6 | 7 | #[cfg(feature = "derive_serde")] 8 | use serde::{Deserialize, Serialize}; 9 | 10 | pub fn random_field_tests(type_name: String) { 11 | let mut rng = XorShiftRng::from_seed([ 12 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 13 | 0xe5, 14 | ]); 15 | 16 | random_multiplication_tests::(&mut rng, type_name.clone()); 17 | random_addition_tests::(&mut rng, type_name.clone()); 18 | random_subtraction_tests::(&mut rng, type_name.clone()); 19 | random_negation_tests::(&mut rng, type_name.clone()); 20 | random_doubling_tests::(&mut rng, type_name.clone()); 21 | random_squaring_tests::(&mut rng, type_name.clone()); 22 | random_inversion_tests::(&mut rng, type_name.clone()); 23 | random_expansion_tests::(&mut rng, type_name); 24 | 25 | assert_eq!(F::ZERO.is_zero().unwrap_u8(), 1); 26 | { 27 | let mut z = F::ZERO; 28 | z = z.neg(); 29 | assert_eq!(z.is_zero().unwrap_u8(), 1); 30 | } 31 | 32 | assert!(bool::from(F::ZERO.invert().is_none())); 33 | 34 | // Multiplication by zero 35 | { 36 | let mut a = F::random(&mut rng); 37 | a.mul_assign(&F::ZERO); 38 | assert_eq!(a.is_zero().unwrap_u8(), 1); 39 | } 40 | 41 | // Addition by zero 42 | { 43 | let mut a = F::random(&mut rng); 44 | let copy = a; 45 | a.add_assign(&F::ZERO); 46 | assert_eq!(a, copy); 47 | } 48 | } 49 | 50 | fn random_multiplication_tests(mut rng: R, type_name: String) { 51 | let _message = format!("multiplication {type_name}"); 52 | let start = start_timer!(|| _message); 53 | for _ in 0..1000000 { 54 | let a = F::random(&mut rng); 55 | let b = F::random(&mut rng); 56 | let c = F::random(&mut rng); 57 | 58 | let mut t0 = a; // (a * b) * c 59 | t0.mul_assign(&b); 60 | t0.mul_assign(&c); 61 | 62 | let mut t1 = a; // (a * c) * b 63 | t1.mul_assign(&c); 64 | t1.mul_assign(&b); 65 | 66 | let mut t2 = b; // (b * c) * a 67 | t2.mul_assign(&c); 68 | t2.mul_assign(&a); 69 | 70 | assert_eq!(t0, t1); 71 | assert_eq!(t1, t2); 72 | } 73 | end_timer!(start); 74 | } 75 | 76 | fn random_addition_tests(mut rng: R, type_name: String) { 77 | let _message = format!("addition {type_name}"); 78 | let start = start_timer!(|| _message); 79 | for _ in 0..1000000 { 80 | let a = F::random(&mut rng); 81 | let b = F::random(&mut rng); 82 | let c = F::random(&mut rng); 83 | 84 | let mut t0 = a; // (a + b) + c 85 | t0.add_assign(&b); 86 | t0.add_assign(&c); 87 | 88 | let mut t1 = a; // (a + c) + b 89 | t1.add_assign(&c); 90 | t1.add_assign(&b); 91 | 92 | let mut t2 = b; // (b + c) + a 93 | t2.add_assign(&c); 94 | t2.add_assign(&a); 95 | 96 | assert_eq!(t0, t1); 97 | assert_eq!(t1, t2); 98 | } 99 | end_timer!(start); 100 | } 101 | 102 | fn random_subtraction_tests(mut rng: R, type_name: String) { 103 | let _message = format!("subtraction {type_name}"); 104 | let start = start_timer!(|| _message); 105 | for _ in 0..1000000 { 106 | let a = F::random(&mut rng); 107 | let b = F::random(&mut rng); 108 | 109 | let mut t0 = a; // (a - b) 110 | t0.sub_assign(&b); 111 | 112 | let mut t1 = b; // (b - a) 113 | t1.sub_assign(&a); 114 | 115 | let mut t2 = t0; // (a - b) + (b - a) = 0 116 | t2.add_assign(&t1); 117 | 118 | assert_eq!(t2.is_zero().unwrap_u8(), 1); 119 | } 120 | end_timer!(start); 121 | } 122 | 123 | fn random_negation_tests(mut rng: R, type_name: String) { 124 | let _message = format!("negation {type_name}"); 125 | let start = start_timer!(|| _message); 126 | for _ in 0..1000000 { 127 | let a = F::random(&mut rng); 128 | let mut b = a; 129 | b = b.neg(); 130 | b.add_assign(&a); 131 | 132 | assert_eq!(b.is_zero().unwrap_u8(), 1); 133 | } 134 | end_timer!(start); 135 | } 136 | 137 | fn random_doubling_tests(mut rng: R, type_name: String) { 138 | let _message = format!("doubling {type_name}"); 139 | let start = start_timer!(|| _message); 140 | for _ in 0..1000000 { 141 | let mut a = F::random(&mut rng); 142 | let mut b = a; 143 | a.add_assign(&b); 144 | b = b.double(); 145 | 146 | assert_eq!(a, b); 147 | } 148 | end_timer!(start); 149 | } 150 | 151 | fn random_squaring_tests(mut rng: R, type_name: String) { 152 | let _message = format!("squaring {type_name}"); 153 | let start = start_timer!(|| _message); 154 | for _ in 0..1000000 { 155 | let mut a = F::random(&mut rng); 156 | let mut b = a; 157 | a.mul_assign(&b); 158 | b = b.square(); 159 | 160 | assert_eq!(a, b); 161 | } 162 | end_timer!(start); 163 | } 164 | 165 | fn random_inversion_tests(mut rng: R, type_name: String) { 166 | assert!(bool::from(F::ZERO.invert().is_none())); 167 | 168 | let _message = format!("inversion {type_name}"); 169 | let start = start_timer!(|| _message); 170 | for _ in 0..1000000 { 171 | let mut a = F::random(&mut rng); 172 | let b = a.invert().unwrap(); // probablistically nonzero 173 | a.mul_assign(&b); 174 | 175 | assert_eq!(a, F::ONE); 176 | } 177 | end_timer!(start); 178 | } 179 | 180 | fn random_expansion_tests(mut rng: R, type_name: String) { 181 | let _message = format!("expansion {type_name}"); 182 | let start = start_timer!(|| _message); 183 | for _ in 0..1000000 { 184 | // Compare (a + b)(c + d) and (a*c + b*c + a*d + b*d) 185 | 186 | let a = F::random(&mut rng); 187 | let b = F::random(&mut rng); 188 | let c = F::random(&mut rng); 189 | let d = F::random(&mut rng); 190 | 191 | let mut t0 = a; 192 | t0.add_assign(&b); 193 | let mut t1 = c; 194 | t1.add_assign(&d); 195 | t0.mul_assign(&t1); 196 | 197 | let mut t2 = a; 198 | t2.mul_assign(&c); 199 | let mut t3 = b; 200 | t3.mul_assign(&c); 201 | let mut t4 = a; 202 | t4.mul_assign(&d); 203 | let mut t5 = b; 204 | t5.mul_assign(&d); 205 | 206 | t2.add_assign(&t3); 207 | t2.add_assign(&t4); 208 | t2.add_assign(&t5); 209 | 210 | assert_eq!(t0, t2); 211 | } 212 | end_timer!(start); 213 | } 214 | 215 | #[cfg(feature = "bits")] 216 | pub fn random_bits_tests(type_name: String) { 217 | let mut rng = XorShiftRng::from_seed([ 218 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 219 | 0xe5, 220 | ]); 221 | let _message = format!("to_le_bits {type_name}"); 222 | let start = start_timer!(|| _message); 223 | for _ in 0..1000000 { 224 | let a = F::random(&mut rng); 225 | let bytes = a.to_repr(); 226 | let bits = a.to_le_bits(); 227 | for idx in 0..bits.len() { 228 | assert_eq!(bits[idx], ((bytes.as_ref()[idx / 8] >> (idx % 8)) & 1) == 1); 229 | } 230 | } 231 | end_timer!(start); 232 | } 233 | 234 | pub fn random_serialization_test(type_name: String) { 235 | let mut rng = XorShiftRng::from_seed([ 236 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 237 | 0xe5, 238 | ]); 239 | let _message = format!("serialization with SerdeObject {type_name}"); 240 | let start = start_timer!(|| _message); 241 | for _ in 0..1000000 { 242 | let a = F::random(&mut rng); 243 | let bytes = a.to_raw_bytes(); 244 | let b = F::from_raw_bytes(&bytes).unwrap(); 245 | assert_eq!(a, b); 246 | let mut buf = Vec::new(); 247 | a.write_raw(&mut buf).unwrap(); 248 | let b = F::read_raw(&mut &buf[..]).unwrap(); 249 | assert_eq!(a, b); 250 | } 251 | end_timer!(start); 252 | } 253 | 254 | #[cfg(feature = "derive_serde")] 255 | pub fn random_serde_test(type_name: String) 256 | where 257 | F: Field + SerdeObject + Serialize + for<'de> Deserialize<'de>, 258 | { 259 | let mut rng = XorShiftRng::from_seed([ 260 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 261 | 0xe5, 262 | ]); 263 | let _message = format!("serialization with serde {type_name}"); 264 | let start = start_timer!(|| _message); 265 | for _ in 0..1000000 { 266 | let a = F::random(&mut rng); 267 | let bytes = bincode::serialize(&a).unwrap(); 268 | let reader = std::io::Cursor::new(bytes); 269 | let b: F = bincode::deserialize_from(reader).unwrap(); 270 | assert_eq!(a, b); 271 | } 272 | end_timer!(start); 273 | } 274 | -------------------------------------------------------------------------------- /src/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use ff::PrimeField; 2 | use num_bigint::BigUint; 3 | use num_traits::Num; 4 | use std::borrow::Cow; 5 | 6 | pub mod curve; 7 | pub mod field; 8 | 9 | pub(crate) fn fe_from_str(string: impl AsRef) -> F { 10 | let string = string.as_ref(); 11 | let oct = if let Some(hex) = string.strip_prefix("0x") { 12 | Cow::Owned(BigUint::from_str_radix(hex, 16).unwrap().to_string()) 13 | } else { 14 | Cow::Borrowed(string) 15 | }; 16 | F::from_str_vartime(&oct).unwrap() 17 | } 18 | --------------------------------------------------------------------------------