├── .github ├── ISSUE_TEMPLATE │ └── eli15.md ├── dependabot.yml └── workflows │ ├── book.yml │ ├── ci.yml │ ├── lints-beta.yml │ └── lints-stable.yml ├── .gitignore ├── CHANGELOG.md ├── COPYING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── benches ├── fp.rs ├── fq.rs ├── hashtocurve.rs └── point.rs ├── book ├── .gitignore ├── Makefile ├── book.toml ├── edithtml.sh └── src │ ├── README.md │ ├── SUMMARY.md │ ├── design.md │ └── design │ ├── implementation.md │ └── implementation │ └── fields.md ├── katex-header.html ├── rust-toolchain.toml └── src ├── arithmetic.rs ├── arithmetic ├── curves.rs └── fields.rs ├── curves.rs ├── fields.rs ├── fields ├── fp.rs └── fq.rs ├── hashtocurve.rs ├── lib.rs ├── macros.rs ├── pallas.rs ├── serde_impl.rs └── vesta.rs /.github/ISSUE_TEMPLATE/eli15.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: ELI15 improvement request 3 | about: Let us know how the Halo 2 book could be improved! 4 | title: 'ELI15: ' 5 | labels: 'ELI15' 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Which section of the Halo 2 book were you reading? 11 | 12 | ## What was unclear? 13 | 14 | ## What would help to make it clearer to you? 15 | 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | timezone: Etc/UTC 8 | open-pull-requests-limit: 10 9 | reviewers: 10 | - str4d 11 | assignees: 12 | - str4d 13 | labels: 14 | - "A-CI" 15 | -------------------------------------------------------------------------------- /.github/workflows/book.yml: -------------------------------------------------------------------------------- 1 | name: Pasta book 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | deploy: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | 14 | - name: Setup mdBook 15 | uses: peaceiris/actions-mdbook@v1 16 | with: 17 | mdbook-version: 'latest' 18 | 19 | - name: Install mdbook-katex 20 | uses: actions-rs/cargo@v1 21 | with: 22 | command: install 23 | args: mdbook-katex 24 | 25 | - name: Build Pasta book 26 | run: mdbook build book/ 27 | 28 | - name: Deploy to GitHub Pages 29 | uses: peaceiris/actions-gh-pages@v3 30 | with: 31 | github_token: ${{ secrets.GITHUB_TOKEN }} 32 | publish_dir: ./book/book 33 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI checks 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test on ${{ matrix.os }} with ${{ matrix.features }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, windows-latest, macOS-latest] 12 | features: [--all-features, --no-default-features] 13 | continue-on-error: true 14 | steps: 15 | - uses: actions/checkout@v4 16 | - name: Run tests 17 | run: cargo test --verbose --release ${{ matrix.features }} 18 | - name: Verify working directory is clean 19 | run: git diff --exit-code 20 | 21 | test-32-bit: 22 | name: Test on i686-unknown-linux-gnu with ${{ matrix.features }} 23 | runs-on: ubuntu-latest 24 | strategy: 25 | matrix: 26 | features: [--all-features, --no-default-features] 27 | continue-on-error: true 28 | steps: 29 | - uses: actions/checkout@v4 30 | - name: Install cross-platform support dependencies 31 | run: sudo apt install gcc-multilib 32 | - run: rustup target add i686-unknown-linux-gnu 33 | - name: Run tests 34 | run: > 35 | cargo test 36 | --verbose 37 | --target i686-unknown-linux-gnu 38 | ${{ matrix.features }} 39 | - name: Verify working directory is clean 40 | run: git diff --exit-code 41 | 42 | no-std: 43 | name: Check no-std target ${{ matrix.target }} 44 | runs-on: ubuntu-latest 45 | strategy: 46 | matrix: 47 | target: 48 | - thumbv6m-none-eabi 49 | - wasm32-unknown-unknown 50 | - wasm32-wasi 51 | 52 | steps: 53 | - uses: actions/checkout@v4 54 | - run: rustup target add ${{ matrix.target }} 55 | - name: Build 56 | run: > 57 | cargo build 58 | --verbose 59 | --target ${{ matrix.target }} 60 | --no-default-features 61 | 62 | bitrot: 63 | name: Bitrot check 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v4 67 | # Build benchmarks to prevent bitrot 68 | - name: Build benchmarks 69 | run: cargo build --benches --all-features 70 | 71 | book: 72 | name: Book tests 73 | runs-on: ubuntu-latest 74 | steps: 75 | - uses: actions/checkout@v4 76 | - run: cargo build 77 | - name: Setup mdBook 78 | uses: peaceiris/actions-mdbook@v1 79 | with: 80 | mdbook-version: '0.4.5' 81 | - name: Test Pasta book 82 | run: mdbook test -L target/debug/deps book/ 83 | 84 | codecov: 85 | name: Code coverage 86 | runs-on: ubuntu-latest 87 | container: 88 | image: xd009642/tarpaulin:develop-nightly 89 | options: --security-opt seccomp=unconfined 90 | steps: 91 | - uses: actions/checkout@v4 92 | - name: Generate coverage report 93 | run: cargo tarpaulin --engine llvm --all-features --timeout 600 --out Xml 94 | - name: Upload coverage to Codecov 95 | uses: codecov/codecov-action@v3.1.1 96 | 97 | doc-links: 98 | name: Intra-doc links 99 | runs-on: ubuntu-latest 100 | steps: 101 | - uses: actions/checkout@v4 102 | - run: cargo fetch 103 | # Requires #![deny(rustdoc::broken_intra_doc_links)] in crates. 104 | - name: Check intra-doc links 105 | run: cargo doc --all-features --document-private-items 106 | 107 | fmt: 108 | name: Rustfmt 109 | runs-on: ubuntu-latest 110 | steps: 111 | - uses: actions/checkout@v4 112 | - run: cargo fmt -- --check 113 | -------------------------------------------------------------------------------- /.github/workflows/lints-beta.yml: -------------------------------------------------------------------------------- 1 | name: Beta lints 2 | 3 | # These lints are only informative, so we only run them directly on branches 4 | # and not trial-merges of PRs, to reduce noise. 5 | on: push 6 | 7 | jobs: 8 | clippy-beta: 9 | name: Clippy (beta) 10 | timeout-minutes: 30 11 | runs-on: ubuntu-latest 12 | continue-on-error: true 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: dtolnay/rust-toolchain@beta 16 | id: toolchain 17 | - run: rustup override set ${{steps.toolchain.outputs.name}} 18 | - name: Run Clippy (beta) 19 | uses: actions-rs/clippy-check@v1 20 | continue-on-error: true 21 | with: 22 | name: Clippy (beta) 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | args: --all-features --all-targets -- -W clippy::all 25 | -------------------------------------------------------------------------------- /.github/workflows/lints-stable.yml: -------------------------------------------------------------------------------- 1 | name: Stable lints 2 | 3 | # We only run these lints on trial-merges of PRs to reduce noise. 4 | on: pull_request 5 | 6 | jobs: 7 | clippy: 8 | name: Clippy (MSRV) 9 | timeout-minutes: 30 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Run clippy 14 | uses: actions-rs/clippy-check@v1 15 | with: 16 | name: Clippy (MSRV) 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | args: --all-features --all-targets -- -D warnings 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | .vscode 4 | **/*.html 5 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to Rust's notion of 6 | [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | ## [Unreleased] 9 | ### Changed 10 | - MSRV is now 1.63.0. 11 | 12 | ## [0.5.1] - 2023-03-02 13 | ### Fixed 14 | - Fix a bug on 32-bit platforms that could cause the square root implementation 15 | to return an incorrect result. 16 | - The `sqrt-table` feature now works without `std` and only requires `alloc`. 17 | 18 | ## [0.5.0] - 2022-12-06 19 | ### Added 20 | - `serde` feature flag, which enables Serde compatibility to the crate types. 21 | Field elements and points are serialized to their canonical byte encoding 22 | (encoded as hexadecimal if the data format is human readable). 23 | 24 | ### Changed 25 | - Migrated to `ff 0.13`, `group 0.13`, `ec-gpu 0.2`. 26 | - `pasta_curves::arithmetic`: 27 | - `FieldExt` bounds on associated types of `CurveExt` and `CurveAffine` have 28 | been replaced by bounds on `ff::WithSmallOrderMulGroup<3>` (and `Ord` in the 29 | case of `CurveExt`). 30 | - `pasta_curves::hashtocurve`: 31 | - `FieldExt` bounds on the module functions have been replaced by equivalent 32 | `ff` trait bounds. 33 | 34 | ### Removed 35 | - `pasta_curves::arithmetic`: 36 | - `FieldExt` (use `ff::PrimeField` or `ff::WithSmallOrderMulGroup` instead). 37 | - `Group` 38 | - `SqrtRatio` (use `ff::Field::{sqrt_ratio, sqrt_alt}` instead). 39 | - `SqrtTables` (from public API, as it isn't suitable for generic usage). 40 | 41 | ## [0.4.1] - 2022-10-13 42 | ### Added 43 | - `uninline-portable` feature flag, which disables inlining of some functions. 44 | This is useful for tiny microchips (such as ARM Cortex-M0), where inlining 45 | can hurt performance and blow up binary size. 46 | 47 | ## [0.4.0] - 2022-05-05 48 | ### Changed 49 | - MSRV is now 1.56.0. 50 | - Migrated to `ff 0.12`, `group 0.12`. 51 | 52 | ## [0.3.1] - 2022-04-20 53 | ### Added 54 | - `gpu` feature flag, which exposes implementations of the `GpuField` trait from 55 | the `ec-gpu` crate for `pasta_curves::{Fp, Fq}`. This flag will eventually 56 | control all GPU functionality. 57 | - `repr-c` feature flag, which helps to facilitate usage of this crate's types 58 | across FFI by conditionally adding `repr(C)` attribute to point structures. 59 | - `pasta_curves::arithmetic::Coordinates::from_xy` 60 | 61 | ### Changed 62 | - `pasta_curves::{Fp, Fq}` are now declared as `repr(transparent)`, to enable 63 | their use across FFI. They remain opaque structs in Rust code. 64 | 65 | ## [0.3.0] - 2022-01-03 66 | ### Added 67 | - Support for `no-std` builds, via two new (default-enabled) feature flags: 68 | - `alloc` enables the `pasta_curves::arithmetic::{CurveAffine, CurveExt}` 69 | traits, as well as implementations of traits like `group::WnafGroup`. 70 | - `sqrt-table` depends on `alloc`, and enables the large precomputed tables 71 | (stored on the heap) that speed up square root computation. 72 | - `pasta_curves::arithmetic::SqrtRatio` trait, extending `ff::PrimeField` with 73 | square roots of ratios. This trait is likely to be moved into the `ff` crate 74 | in a future release (once we're satisfied with it). 75 | 76 | ### Removed 77 | - `pasta_curves::arithmetic`: 78 | - `Field` re-export (`pasta_curves::group::ff::Field` is equivalent). 79 | - `FieldExt::ROOT_OF_UNITY` (use `ff::PrimeField::root_of_unity` instead). 80 | - `FieldExt::{T_MINUS1_OVER2, pow_by_t_minus1_over2, get_lower_32, sqrt_alt,` 81 | `sqrt_ratio}` (moved to `SqrtRatio` trait). 82 | - `FieldExt::{RESCUE_ALPHA, RESCUE_INVALPHA}` 83 | - `FieldExt::from_u64` (use `From for ff::PrimeField` instead). 84 | - `FieldExt::{from_bytes, read, to_bytes, write}` 85 | (use `ff::PrimeField::{from_repr, to_repr}` instead). 86 | - `FieldExt::rand` (use `ff::Field::random` instead). 87 | - `CurveAffine::{read, write}` 88 | (use `group::GroupEncoding::{from_bytes, to_bytes}` instead). 89 | 90 | ## [0.2.1] - 2021-09-17 91 | ### Changed 92 | - The crate is now licensed as `MIT OR Apache-2.0`. 93 | 94 | ## [0.2.0] - 2021-09-02 95 | ### Changed 96 | - Migrated to `ff 0.11`, `group 0.11`. 97 | 98 | ## [0.1.2] - 2021-08-06 99 | ### Added 100 | - Implementation of `group::WnafGroup` for Pallas and Vesta, enabling them to be 101 | used with `group::Wnaf` for targeted performance improvements. 102 | 103 | ## [0.1.1] - 2021-06-04 104 | ### Added 105 | - Implementations of `group::cofactor::{CofactorCurve, CofactorCurveAffine}` for 106 | Pallas and Vesta, enabling them to be used in cofactor-aware protocols that 107 | also want to leverage the affine point representation. 108 | 109 | ## [0.1.0] - 2021-06-01 110 | Initial release! 111 | -------------------------------------------------------------------------------- /COPYING.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Licensed under either of 4 | 5 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 6 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 7 | 8 | at your option. 9 | 10 | # Contribution 11 | 12 | Unless you explicitly state otherwise, any contribution intentionally 13 | submitted for inclusion in the work by you, as defined in the Apache-2.0 14 | license, shall be dual licensed as above, without any additional terms or 15 | conditions. 16 | 17 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.3" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anes" 16 | version = "0.1.6" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 19 | 20 | [[package]] 21 | name = "arrayref" 22 | version = "0.3.9" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" 25 | 26 | [[package]] 27 | name = "arrayvec" 28 | version = "0.7.6" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 31 | 32 | [[package]] 33 | name = "atty" 34 | version = "0.2.14" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 37 | dependencies = [ 38 | "hermit-abi", 39 | "libc", 40 | "winapi", 41 | ] 42 | 43 | [[package]] 44 | name = "autocfg" 45 | version = "1.4.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 48 | 49 | [[package]] 50 | name = "bincode" 51 | version = "1.3.3" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 54 | dependencies = [ 55 | "serde", 56 | ] 57 | 58 | [[package]] 59 | name = "bitflags" 60 | version = "1.3.2" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 63 | 64 | [[package]] 65 | name = "bitvec" 66 | version = "1.0.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" 69 | dependencies = [ 70 | "funty", 71 | "radium", 72 | "tap", 73 | "wyz", 74 | ] 75 | 76 | [[package]] 77 | name = "blake2b_simd" 78 | version = "1.0.1" 79 | source = "registry+https://github.com/rust-lang/crates.io-index" 80 | checksum = "3c2f0dc9a68c6317d884f97cc36cf5a3d20ba14ce404227df55e1af708ab04bc" 81 | dependencies = [ 82 | "arrayref", 83 | "arrayvec", 84 | "constant_time_eq", 85 | ] 86 | 87 | [[package]] 88 | name = "bumpalo" 89 | version = "3.17.0" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" 92 | 93 | [[package]] 94 | name = "cast" 95 | version = "0.3.0" 96 | source = "registry+https://github.com/rust-lang/crates.io-index" 97 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 98 | 99 | [[package]] 100 | name = "cfg-if" 101 | version = "1.0.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 104 | 105 | [[package]] 106 | name = "ciborium" 107 | version = "0.2.2" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 110 | dependencies = [ 111 | "ciborium-io", 112 | "ciborium-ll", 113 | "serde", 114 | ] 115 | 116 | [[package]] 117 | name = "ciborium-io" 118 | version = "0.2.2" 119 | source = "registry+https://github.com/rust-lang/crates.io-index" 120 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 121 | 122 | [[package]] 123 | name = "ciborium-ll" 124 | version = "0.2.2" 125 | source = "registry+https://github.com/rust-lang/crates.io-index" 126 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 127 | dependencies = [ 128 | "ciborium-io", 129 | "half", 130 | ] 131 | 132 | [[package]] 133 | name = "clap" 134 | version = "3.2.25" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" 137 | dependencies = [ 138 | "bitflags", 139 | "clap_lex", 140 | "indexmap", 141 | "textwrap", 142 | ] 143 | 144 | [[package]] 145 | name = "clap_lex" 146 | version = "0.2.4" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 149 | dependencies = [ 150 | "os_str_bytes", 151 | ] 152 | 153 | [[package]] 154 | name = "constant_time_eq" 155 | version = "0.2.6" 156 | source = "registry+https://github.com/rust-lang/crates.io-index" 157 | checksum = "21a53c0a4d288377e7415b53dcfc3c04da5cdc2cc95c8d5ac178b58f0b861ad6" 158 | 159 | [[package]] 160 | name = "criterion" 161 | version = "0.4.0" 162 | source = "registry+https://github.com/rust-lang/crates.io-index" 163 | checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" 164 | dependencies = [ 165 | "anes", 166 | "atty", 167 | "cast", 168 | "ciborium", 169 | "clap", 170 | "criterion-plot", 171 | "itertools", 172 | "lazy_static", 173 | "num-traits", 174 | "oorandom", 175 | "plotters", 176 | "rayon", 177 | "regex", 178 | "serde", 179 | "serde_derive", 180 | "serde_json", 181 | "tinytemplate", 182 | "walkdir", 183 | ] 184 | 185 | [[package]] 186 | name = "criterion-plot" 187 | version = "0.5.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 190 | dependencies = [ 191 | "cast", 192 | "itertools", 193 | ] 194 | 195 | [[package]] 196 | name = "crossbeam-deque" 197 | version = "0.8.6" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" 200 | dependencies = [ 201 | "crossbeam-epoch", 202 | "crossbeam-utils", 203 | ] 204 | 205 | [[package]] 206 | name = "crossbeam-epoch" 207 | version = "0.9.18" 208 | source = "registry+https://github.com/rust-lang/crates.io-index" 209 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 210 | dependencies = [ 211 | "crossbeam-utils", 212 | ] 213 | 214 | [[package]] 215 | name = "crossbeam-utils" 216 | version = "0.8.21" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 219 | 220 | [[package]] 221 | name = "crunchy" 222 | version = "0.2.3" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" 225 | 226 | [[package]] 227 | name = "ec-gpu" 228 | version = "0.2.0" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "bd63582de2b59ea1aa48d7c1941b5d87618d95484397521b3acdfa0e1e9f5e45" 231 | 232 | [[package]] 233 | name = "either" 234 | version = "1.15.0" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" 237 | 238 | [[package]] 239 | name = "ff" 240 | version = "0.13.1" 241 | source = "registry+https://github.com/rust-lang/crates.io-index" 242 | checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" 243 | dependencies = [ 244 | "bitvec", 245 | "rand_core", 246 | "subtle", 247 | ] 248 | 249 | [[package]] 250 | name = "funty" 251 | version = "2.0.0" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" 254 | 255 | [[package]] 256 | name = "group" 257 | version = "0.13.0" 258 | source = "registry+https://github.com/rust-lang/crates.io-index" 259 | checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" 260 | dependencies = [ 261 | "ff", 262 | "rand_core", 263 | "subtle", 264 | ] 265 | 266 | [[package]] 267 | name = "half" 268 | version = "2.2.1" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0" 271 | dependencies = [ 272 | "crunchy", 273 | ] 274 | 275 | [[package]] 276 | name = "hashbrown" 277 | version = "0.12.3" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 280 | 281 | [[package]] 282 | name = "hermit-abi" 283 | version = "0.1.19" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 286 | dependencies = [ 287 | "libc", 288 | ] 289 | 290 | [[package]] 291 | name = "hex" 292 | version = "0.4.3" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 295 | dependencies = [ 296 | "serde", 297 | ] 298 | 299 | [[package]] 300 | name = "indexmap" 301 | version = "1.9.3" 302 | source = "registry+https://github.com/rust-lang/crates.io-index" 303 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 304 | dependencies = [ 305 | "autocfg", 306 | "hashbrown", 307 | ] 308 | 309 | [[package]] 310 | name = "itertools" 311 | version = "0.10.5" 312 | source = "registry+https://github.com/rust-lang/crates.io-index" 313 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 314 | dependencies = [ 315 | "either", 316 | ] 317 | 318 | [[package]] 319 | name = "itoa" 320 | version = "1.0.15" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 323 | 324 | [[package]] 325 | name = "js-sys" 326 | version = "0.3.77" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" 329 | dependencies = [ 330 | "once_cell", 331 | "wasm-bindgen", 332 | ] 333 | 334 | [[package]] 335 | name = "lazy_static" 336 | version = "1.5.0" 337 | source = "registry+https://github.com/rust-lang/crates.io-index" 338 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 339 | dependencies = [ 340 | "spin", 341 | ] 342 | 343 | [[package]] 344 | name = "libc" 345 | version = "0.2.172" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" 348 | 349 | [[package]] 350 | name = "log" 351 | version = "0.4.27" 352 | source = "registry+https://github.com/rust-lang/crates.io-index" 353 | checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" 354 | 355 | [[package]] 356 | name = "memchr" 357 | version = "2.7.4" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 360 | 361 | [[package]] 362 | name = "num-traits" 363 | version = "0.2.19" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 366 | dependencies = [ 367 | "autocfg", 368 | ] 369 | 370 | [[package]] 371 | name = "once_cell" 372 | version = "1.21.3" 373 | source = "registry+https://github.com/rust-lang/crates.io-index" 374 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 375 | 376 | [[package]] 377 | name = "oorandom" 378 | version = "11.1.5" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" 381 | 382 | [[package]] 383 | name = "os_str_bytes" 384 | version = "6.6.1" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" 387 | 388 | [[package]] 389 | name = "pasta_curves" 390 | version = "0.5.1" 391 | dependencies = [ 392 | "bincode", 393 | "blake2b_simd", 394 | "criterion", 395 | "ec-gpu", 396 | "ff", 397 | "group", 398 | "hex", 399 | "lazy_static", 400 | "rand", 401 | "rand_xorshift", 402 | "serde", 403 | "serde_json", 404 | "static_assertions", 405 | "subtle", 406 | ] 407 | 408 | [[package]] 409 | name = "plotters" 410 | version = "0.3.7" 411 | source = "registry+https://github.com/rust-lang/crates.io-index" 412 | checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" 413 | dependencies = [ 414 | "num-traits", 415 | "plotters-backend", 416 | "plotters-svg", 417 | "wasm-bindgen", 418 | "web-sys", 419 | ] 420 | 421 | [[package]] 422 | name = "plotters-backend" 423 | version = "0.3.7" 424 | source = "registry+https://github.com/rust-lang/crates.io-index" 425 | checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" 426 | 427 | [[package]] 428 | name = "plotters-svg" 429 | version = "0.3.7" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" 432 | dependencies = [ 433 | "plotters-backend", 434 | ] 435 | 436 | [[package]] 437 | name = "proc-macro2" 438 | version = "1.0.95" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" 441 | dependencies = [ 442 | "unicode-ident", 443 | ] 444 | 445 | [[package]] 446 | name = "quote" 447 | version = "1.0.40" 448 | source = "registry+https://github.com/rust-lang/crates.io-index" 449 | checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" 450 | dependencies = [ 451 | "proc-macro2", 452 | ] 453 | 454 | [[package]] 455 | name = "radium" 456 | version = "0.7.0" 457 | source = "registry+https://github.com/rust-lang/crates.io-index" 458 | checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" 459 | 460 | [[package]] 461 | name = "rand" 462 | version = "0.8.5" 463 | source = "registry+https://github.com/rust-lang/crates.io-index" 464 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 465 | dependencies = [ 466 | "rand_core", 467 | ] 468 | 469 | [[package]] 470 | name = "rand_core" 471 | version = "0.6.4" 472 | source = "registry+https://github.com/rust-lang/crates.io-index" 473 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 474 | 475 | [[package]] 476 | name = "rand_xorshift" 477 | version = "0.3.0" 478 | source = "registry+https://github.com/rust-lang/crates.io-index" 479 | checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" 480 | dependencies = [ 481 | "rand_core", 482 | ] 483 | 484 | [[package]] 485 | name = "rayon" 486 | version = "1.10.0" 487 | source = "registry+https://github.com/rust-lang/crates.io-index" 488 | checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" 489 | dependencies = [ 490 | "either", 491 | "rayon-core", 492 | ] 493 | 494 | [[package]] 495 | name = "rayon-core" 496 | version = "1.12.1" 497 | source = "registry+https://github.com/rust-lang/crates.io-index" 498 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 499 | dependencies = [ 500 | "crossbeam-deque", 501 | "crossbeam-utils", 502 | ] 503 | 504 | [[package]] 505 | name = "regex" 506 | version = "1.9.6" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" 509 | dependencies = [ 510 | "aho-corasick", 511 | "memchr", 512 | "regex-automata", 513 | "regex-syntax", 514 | ] 515 | 516 | [[package]] 517 | name = "regex-automata" 518 | version = "0.3.9" 519 | source = "registry+https://github.com/rust-lang/crates.io-index" 520 | checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" 521 | dependencies = [ 522 | "aho-corasick", 523 | "memchr", 524 | "regex-syntax", 525 | ] 526 | 527 | [[package]] 528 | name = "regex-syntax" 529 | version = "0.7.5" 530 | source = "registry+https://github.com/rust-lang/crates.io-index" 531 | checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" 532 | 533 | [[package]] 534 | name = "rustversion" 535 | version = "1.0.20" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" 538 | 539 | [[package]] 540 | name = "ryu" 541 | version = "1.0.20" 542 | source = "registry+https://github.com/rust-lang/crates.io-index" 543 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 544 | 545 | [[package]] 546 | name = "same-file" 547 | version = "1.0.6" 548 | source = "registry+https://github.com/rust-lang/crates.io-index" 549 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 550 | dependencies = [ 551 | "winapi-util", 552 | ] 553 | 554 | [[package]] 555 | name = "serde" 556 | version = "1.0.219" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" 559 | dependencies = [ 560 | "serde_derive", 561 | ] 562 | 563 | [[package]] 564 | name = "serde_derive" 565 | version = "1.0.219" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" 568 | dependencies = [ 569 | "proc-macro2", 570 | "quote", 571 | "syn", 572 | ] 573 | 574 | [[package]] 575 | name = "serde_json" 576 | version = "1.0.140" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" 579 | dependencies = [ 580 | "itoa", 581 | "memchr", 582 | "ryu", 583 | "serde", 584 | ] 585 | 586 | [[package]] 587 | name = "spin" 588 | version = "0.9.8" 589 | source = "registry+https://github.com/rust-lang/crates.io-index" 590 | checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" 591 | 592 | [[package]] 593 | name = "static_assertions" 594 | version = "1.1.0" 595 | source = "registry+https://github.com/rust-lang/crates.io-index" 596 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 597 | 598 | [[package]] 599 | name = "subtle" 600 | version = "2.6.1" 601 | source = "registry+https://github.com/rust-lang/crates.io-index" 602 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 603 | 604 | [[package]] 605 | name = "syn" 606 | version = "2.0.100" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" 609 | dependencies = [ 610 | "proc-macro2", 611 | "quote", 612 | "unicode-ident", 613 | ] 614 | 615 | [[package]] 616 | name = "tap" 617 | version = "1.0.1" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" 620 | 621 | [[package]] 622 | name = "textwrap" 623 | version = "0.16.1" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" 626 | 627 | [[package]] 628 | name = "tinytemplate" 629 | version = "1.2.1" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 632 | dependencies = [ 633 | "serde", 634 | "serde_json", 635 | ] 636 | 637 | [[package]] 638 | name = "unicode-ident" 639 | version = "1.0.18" 640 | source = "registry+https://github.com/rust-lang/crates.io-index" 641 | checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" 642 | 643 | [[package]] 644 | name = "walkdir" 645 | version = "2.5.0" 646 | source = "registry+https://github.com/rust-lang/crates.io-index" 647 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 648 | dependencies = [ 649 | "same-file", 650 | "winapi-util", 651 | ] 652 | 653 | [[package]] 654 | name = "wasm-bindgen" 655 | version = "0.2.100" 656 | source = "registry+https://github.com/rust-lang/crates.io-index" 657 | checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" 658 | dependencies = [ 659 | "cfg-if", 660 | "once_cell", 661 | "rustversion", 662 | "wasm-bindgen-macro", 663 | ] 664 | 665 | [[package]] 666 | name = "wasm-bindgen-backend" 667 | version = "0.2.100" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" 670 | dependencies = [ 671 | "bumpalo", 672 | "log", 673 | "proc-macro2", 674 | "quote", 675 | "syn", 676 | "wasm-bindgen-shared", 677 | ] 678 | 679 | [[package]] 680 | name = "wasm-bindgen-macro" 681 | version = "0.2.100" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" 684 | dependencies = [ 685 | "quote", 686 | "wasm-bindgen-macro-support", 687 | ] 688 | 689 | [[package]] 690 | name = "wasm-bindgen-macro-support" 691 | version = "0.2.100" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" 694 | dependencies = [ 695 | "proc-macro2", 696 | "quote", 697 | "syn", 698 | "wasm-bindgen-backend", 699 | "wasm-bindgen-shared", 700 | ] 701 | 702 | [[package]] 703 | name = "wasm-bindgen-shared" 704 | version = "0.2.100" 705 | source = "registry+https://github.com/rust-lang/crates.io-index" 706 | checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" 707 | dependencies = [ 708 | "unicode-ident", 709 | ] 710 | 711 | [[package]] 712 | name = "web-sys" 713 | version = "0.3.77" 714 | source = "registry+https://github.com/rust-lang/crates.io-index" 715 | checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" 716 | dependencies = [ 717 | "js-sys", 718 | "wasm-bindgen", 719 | ] 720 | 721 | [[package]] 722 | name = "winapi" 723 | version = "0.3.9" 724 | source = "registry+https://github.com/rust-lang/crates.io-index" 725 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 726 | dependencies = [ 727 | "winapi-i686-pc-windows-gnu", 728 | "winapi-x86_64-pc-windows-gnu", 729 | ] 730 | 731 | [[package]] 732 | name = "winapi-i686-pc-windows-gnu" 733 | version = "0.4.0" 734 | source = "registry+https://github.com/rust-lang/crates.io-index" 735 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 736 | 737 | [[package]] 738 | name = "winapi-util" 739 | version = "0.1.9" 740 | source = "registry+https://github.com/rust-lang/crates.io-index" 741 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 742 | dependencies = [ 743 | "windows-sys", 744 | ] 745 | 746 | [[package]] 747 | name = "winapi-x86_64-pc-windows-gnu" 748 | version = "0.4.0" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 751 | 752 | [[package]] 753 | name = "windows-sys" 754 | version = "0.59.0" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 757 | dependencies = [ 758 | "windows-targets", 759 | ] 760 | 761 | [[package]] 762 | name = "windows-targets" 763 | version = "0.52.6" 764 | source = "registry+https://github.com/rust-lang/crates.io-index" 765 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 766 | dependencies = [ 767 | "windows_aarch64_gnullvm", 768 | "windows_aarch64_msvc", 769 | "windows_i686_gnu", 770 | "windows_i686_gnullvm", 771 | "windows_i686_msvc", 772 | "windows_x86_64_gnu", 773 | "windows_x86_64_gnullvm", 774 | "windows_x86_64_msvc", 775 | ] 776 | 777 | [[package]] 778 | name = "windows_aarch64_gnullvm" 779 | version = "0.52.6" 780 | source = "registry+https://github.com/rust-lang/crates.io-index" 781 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 782 | 783 | [[package]] 784 | name = "windows_aarch64_msvc" 785 | version = "0.52.6" 786 | source = "registry+https://github.com/rust-lang/crates.io-index" 787 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 788 | 789 | [[package]] 790 | name = "windows_i686_gnu" 791 | version = "0.52.6" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 794 | 795 | [[package]] 796 | name = "windows_i686_gnullvm" 797 | version = "0.52.6" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 800 | 801 | [[package]] 802 | name = "windows_i686_msvc" 803 | version = "0.52.6" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 806 | 807 | [[package]] 808 | name = "windows_x86_64_gnu" 809 | version = "0.52.6" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 812 | 813 | [[package]] 814 | name = "windows_x86_64_gnullvm" 815 | version = "0.52.6" 816 | source = "registry+https://github.com/rust-lang/crates.io-index" 817 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 818 | 819 | [[package]] 820 | name = "windows_x86_64_msvc" 821 | version = "0.52.6" 822 | source = "registry+https://github.com/rust-lang/crates.io-index" 823 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 824 | 825 | [[package]] 826 | name = "wyz" 827 | version = "0.5.1" 828 | source = "registry+https://github.com/rust-lang/crates.io-index" 829 | checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" 830 | dependencies = [ 831 | "tap", 832 | ] 833 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pasta_curves" 3 | description = "Implementation of the Pallas and Vesta (Pasta) curve cycle" 4 | version = "0.5.1" 5 | authors = [ 6 | "Sean Bowe ", 7 | "Ying Tong Lai ", 8 | "Daira Hopwood ", 9 | "Jack Grigg ", 10 | ] 11 | edition = "2021" 12 | rust-version = "1.63" 13 | license = "MIT OR Apache-2.0" 14 | repository = "https://github.com/zcash/pasta_curves" 15 | documentation = "https://docs.rs/pasta_curves" 16 | readme = "README.md" 17 | 18 | [package.metadata.docs.rs] 19 | all-features = true 20 | rustdoc-args = ["--cfg", "docsrs", "--html-in-header", "katex-header.html"] 21 | 22 | [dev-dependencies] 23 | bincode = "1.3" 24 | criterion = "0.4" 25 | rand_xorshift = "0.3" 26 | serde_json = "1.0" 27 | 28 | [[bench]] 29 | name = "hashtocurve" 30 | harness = false 31 | required-features = ["alloc"] 32 | 33 | [[bench]] 34 | name = "fp" 35 | harness = false 36 | 37 | [[bench]] 38 | name = "fq" 39 | harness = false 40 | 41 | [[bench]] 42 | name = "point" 43 | harness = false 44 | required-features = ["alloc"] 45 | 46 | [dependencies] 47 | ff = { version = "0.13", default-features = false } 48 | group = { version = "0.13", default-features = false } 49 | rand = { version = "0.8", default-features = false } 50 | static_assertions = "1.1.0" 51 | subtle = { version = "2.3", default-features = false } 52 | 53 | # alloc dependencies 54 | blake2b_simd = { version = "1", optional = true, default-features = false } 55 | 56 | # sqrt-table dependencies 57 | lazy_static = { version = "1.4.0", optional = true, features = ["spin_no_std"] } 58 | 59 | # gpu dependencies 60 | ec-gpu = { version = "0.2.0", optional = true } 61 | 62 | # serde dependencies 63 | serde_crate = { version = "1.0.16", optional = true, default-features = false, features = ["alloc"], package = "serde" } 64 | hex = { version = "0.4", optional = true, default-features = false, features = ["alloc", "serde"] } 65 | 66 | [features] 67 | default = ["bits", "sqrt-table"] 68 | alloc = ["group/alloc", "blake2b_simd"] 69 | bits = ["ff/bits"] 70 | gpu = ["alloc", "ec-gpu"] 71 | sqrt-table = ["alloc", "lazy_static"] 72 | repr-c = [] 73 | uninline-portable = [] 74 | serde = ["hex", "serde_crate"] 75 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020-2021 The Electric Coin Company 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `pasta_curves` 2 | 3 | This crate provides an implementation of the Pasta elliptic curve constructions, 4 | Pallas and Vesta. More details about the Pasta curves can be found 5 | [in this blog post](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/). 6 | 7 | ## RFC process 8 | 9 | This crate follows the [zkcrypto RFC process](https://zkcrypto.github.io/rfcs/). 10 | If you want to propose "substantial" changes to this crate, please 11 | [create an RFC](https://github.com/zkcrypto/rfcs) for wider discussion. 12 | 13 | ## [Documentation](https://docs.rs/pasta_curves) 14 | 15 | ## Minimum Supported Rust Version 16 | 17 | Requires Rust **1.63** or higher. 18 | 19 | Minimum supported Rust version can be changed in the future, but it will be done with a 20 | minor version bump. 21 | 22 | ## Curve Descriptions 23 | 24 | - Pallas: y2 = x3 + 5 over 25 | `GF(0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001)`. 26 | 27 | - Vesta: y2 = x3 + 5 over 28 | `GF(0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001)`. 29 | 30 | The Pasta curves form a cycle with one another: the order of each curve is exactly the 31 | base field of the other. This property is critical to the efficiency of recursive proof 32 | systems. They are designed to be highly 2-adic, meaning that a large power-of-two 33 | multiplicative subgroup exists in each field. This is important for the performance of 34 | polynomial arithmetic over their scalar fields and is essential for protocols similar 35 | to PLONK. 36 | 37 | These curves can be reproducibly obtained 38 | [using a curve search utility we’ve published](https://github.com/zcash/pasta). 39 | 40 | ## License 41 | 42 | Licensed under either of 43 | 44 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or 45 | http://www.apache.org/licenses/LICENSE-2.0) 46 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 47 | 48 | at your option. 49 | 50 | ### Contribution 51 | 52 | Unless you explicitly state otherwise, any contribution intentionally 53 | submitted for inclusion in the work by you, as defined in the Apache-2.0 54 | license, shall be dual licensed as above, without any additional terms or 55 | conditions. 56 | -------------------------------------------------------------------------------- /benches/fp.rs: -------------------------------------------------------------------------------- 1 | ///! Benchmarks for the Fp field. 2 | use criterion::{criterion_group, criterion_main, Bencher, Criterion}; 3 | 4 | use rand::SeedableRng; 5 | use rand_xorshift::XorShiftRng; 6 | 7 | use ff::{Field, PrimeField}; 8 | use pasta_curves::Fp; 9 | 10 | fn criterion_benchmark(c: &mut Criterion) { 11 | let mut group = c.benchmark_group("Fp"); 12 | 13 | group.bench_function("double", bench_fp_double); 14 | group.bench_function("add_assign", bench_fp_add_assign); 15 | group.bench_function("sub_assign", bench_fp_sub_assign); 16 | group.bench_function("mul_assign", bench_fp_mul_assign); 17 | group.bench_function("square", bench_fp_square); 18 | group.bench_function("invert", bench_fp_invert); 19 | group.bench_function("neg", bench_fp_neg); 20 | group.bench_function("sqrt", bench_fp_sqrt); 21 | group.bench_function("to_repr", bench_fp_to_repr); 22 | group.bench_function("from_repr", bench_fp_from_repr); 23 | } 24 | 25 | fn bench_fp_double(b: &mut Bencher) { 26 | const SAMPLES: usize = 1000; 27 | 28 | let mut rng = XorShiftRng::from_seed([ 29 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 30 | 0xe5, 31 | ]); 32 | 33 | let v: Vec = (0..SAMPLES).map(|_| Fp::random(&mut rng)).collect(); 34 | 35 | let mut count = 0; 36 | b.iter(|| { 37 | let mut tmp = v[count]; 38 | tmp = tmp.double(); 39 | count = (count + 1) % SAMPLES; 40 | tmp 41 | }); 42 | } 43 | 44 | fn bench_fp_add_assign(b: &mut Bencher) { 45 | const SAMPLES: usize = 1000; 46 | 47 | let mut rng = XorShiftRng::from_seed([ 48 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 49 | 0xe5, 50 | ]); 51 | 52 | let v: Vec<(Fp, Fp)> = (0..SAMPLES) 53 | .map(|_| (Fp::random(&mut rng), Fp::random(&mut rng))) 54 | .collect(); 55 | 56 | let mut count = 0; 57 | b.iter(|| { 58 | let mut tmp = v[count].0; 59 | tmp += &v[count].1; 60 | count = (count + 1) % SAMPLES; 61 | tmp 62 | }); 63 | } 64 | 65 | fn bench_fp_sub_assign(b: &mut Bencher) { 66 | const SAMPLES: usize = 1000; 67 | 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 | 73 | let v: Vec<(Fp, Fp)> = (0..SAMPLES) 74 | .map(|_| (Fp::random(&mut rng), Fp::random(&mut rng))) 75 | .collect(); 76 | 77 | let mut count = 0; 78 | b.iter(|| { 79 | let mut tmp = v[count].0; 80 | tmp -= &v[count].1; 81 | count = (count + 1) % SAMPLES; 82 | tmp 83 | }); 84 | } 85 | 86 | fn bench_fp_mul_assign(b: &mut Bencher) { 87 | const SAMPLES: usize = 1000; 88 | 89 | let mut rng = XorShiftRng::from_seed([ 90 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 91 | 0xe5, 92 | ]); 93 | 94 | let v: Vec<(Fp, Fp)> = (0..SAMPLES) 95 | .map(|_| (Fp::random(&mut rng), Fp::random(&mut rng))) 96 | .collect(); 97 | 98 | let mut count = 0; 99 | b.iter(|| { 100 | let mut tmp = v[count].0; 101 | tmp *= &v[count].1; 102 | count = (count + 1) % SAMPLES; 103 | tmp 104 | }); 105 | } 106 | 107 | fn bench_fp_square(b: &mut Bencher) { 108 | const SAMPLES: usize = 1000; 109 | 110 | let mut rng = XorShiftRng::from_seed([ 111 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 112 | 0xe5, 113 | ]); 114 | 115 | let v: Vec = (0..SAMPLES).map(|_| Fp::random(&mut rng)).collect(); 116 | 117 | let mut count = 0; 118 | b.iter(|| { 119 | let mut tmp = v[count]; 120 | tmp = tmp.square(); 121 | count = (count + 1) % SAMPLES; 122 | tmp 123 | }); 124 | } 125 | 126 | fn bench_fp_invert(b: &mut Bencher) { 127 | const SAMPLES: usize = 1000; 128 | 129 | let mut rng = XorShiftRng::from_seed([ 130 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 131 | 0xe5, 132 | ]); 133 | 134 | let v: Vec = (0..SAMPLES).map(|_| Fp::random(&mut rng)).collect(); 135 | 136 | let mut count = 0; 137 | b.iter(|| { 138 | count = (count + 1) % SAMPLES; 139 | v[count].invert() 140 | }); 141 | } 142 | 143 | fn bench_fp_neg(b: &mut Bencher) { 144 | const SAMPLES: usize = 1000; 145 | 146 | let mut rng = XorShiftRng::from_seed([ 147 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 148 | 0xe5, 149 | ]); 150 | 151 | let v: Vec = (0..SAMPLES).map(|_| Fp::random(&mut rng)).collect(); 152 | 153 | let mut count = 0; 154 | b.iter(|| { 155 | let mut tmp = v[count]; 156 | tmp = tmp.neg(); 157 | count = (count + 1) % SAMPLES; 158 | tmp 159 | }); 160 | } 161 | 162 | fn bench_fp_sqrt(b: &mut Bencher) { 163 | const SAMPLES: usize = 1000; 164 | 165 | let mut rng = XorShiftRng::from_seed([ 166 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 167 | 0xe5, 168 | ]); 169 | 170 | let v: Vec = (0..SAMPLES) 171 | .map(|_| { 172 | let tmp = Fp::random(&mut rng); 173 | tmp.square() 174 | }) 175 | .collect(); 176 | 177 | let mut count = 0; 178 | b.iter(|| { 179 | count = (count + 1) % SAMPLES; 180 | v[count].sqrt() 181 | }); 182 | } 183 | 184 | fn bench_fp_to_repr(b: &mut Bencher) { 185 | const SAMPLES: usize = 1000; 186 | 187 | let mut rng = XorShiftRng::from_seed([ 188 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 189 | 0xe5, 190 | ]); 191 | 192 | let v: Vec = (0..SAMPLES).map(|_| Fp::random(&mut rng)).collect(); 193 | 194 | let mut count = 0; 195 | b.iter(|| { 196 | count = (count + 1) % SAMPLES; 197 | v[count].to_repr() 198 | }); 199 | } 200 | 201 | fn bench_fp_from_repr(b: &mut Bencher) { 202 | const SAMPLES: usize = 1000; 203 | 204 | let mut rng = XorShiftRng::from_seed([ 205 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 206 | 0xe5, 207 | ]); 208 | 209 | let v: Vec<::Repr> = (0..SAMPLES) 210 | .map(|_| Fp::random(&mut rng).to_repr()) 211 | .collect(); 212 | 213 | let mut count = 0; 214 | b.iter(|| { 215 | count = (count + 1) % SAMPLES; 216 | Fp::from_repr(v[count]) 217 | }); 218 | } 219 | 220 | criterion_group!(benches, criterion_benchmark); 221 | criterion_main!(benches); 222 | -------------------------------------------------------------------------------- /benches/fq.rs: -------------------------------------------------------------------------------- 1 | ///! Benchmarks for the Fq field. 2 | use criterion::{criterion_group, criterion_main, Bencher, Criterion}; 3 | 4 | use rand::SeedableRng; 5 | use rand_xorshift::XorShiftRng; 6 | 7 | use ff::{Field, PrimeField}; 8 | use pasta_curves::Fq; 9 | 10 | fn criterion_benchmark(c: &mut Criterion) { 11 | let mut group = c.benchmark_group("Fq"); 12 | 13 | group.bench_function("double", bench_fq_double); 14 | group.bench_function("add_assign", bench_fq_add_assign); 15 | group.bench_function("sub_assign", bench_fq_sub_assign); 16 | group.bench_function("mul_assign", bench_fq_mul_assign); 17 | group.bench_function("square", bench_fq_square); 18 | group.bench_function("invert", bench_fq_invert); 19 | group.bench_function("neg", bench_fq_neg); 20 | group.bench_function("sqrt", bench_fq_sqrt); 21 | group.bench_function("to_repr", bench_fq_to_repr); 22 | group.bench_function("from_repr", bench_fq_from_repr); 23 | } 24 | 25 | fn bench_fq_double(b: &mut Bencher) { 26 | const SAMPLES: usize = 1000; 27 | 28 | let mut rng = XorShiftRng::from_seed([ 29 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 30 | 0xe5, 31 | ]); 32 | 33 | let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); 34 | 35 | let mut count = 0; 36 | b.iter(|| { 37 | let mut tmp = v[count]; 38 | tmp = tmp.double(); 39 | count = (count + 1) % SAMPLES; 40 | tmp 41 | }); 42 | } 43 | 44 | fn bench_fq_add_assign(b: &mut Bencher) { 45 | const SAMPLES: usize = 1000; 46 | 47 | let mut rng = XorShiftRng::from_seed([ 48 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 49 | 0xe5, 50 | ]); 51 | 52 | let v: Vec<(Fq, Fq)> = (0..SAMPLES) 53 | .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) 54 | .collect(); 55 | 56 | let mut count = 0; 57 | b.iter(|| { 58 | let mut tmp = v[count].0; 59 | tmp += &v[count].1; 60 | count = (count + 1) % SAMPLES; 61 | tmp 62 | }); 63 | } 64 | 65 | fn bench_fq_sub_assign(b: &mut Bencher) { 66 | const SAMPLES: usize = 1000; 67 | 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 | 73 | let v: Vec<(Fq, Fq)> = (0..SAMPLES) 74 | .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) 75 | .collect(); 76 | 77 | let mut count = 0; 78 | b.iter(|| { 79 | let mut tmp = v[count].0; 80 | tmp -= &v[count].1; 81 | count = (count + 1) % SAMPLES; 82 | tmp 83 | }); 84 | } 85 | 86 | fn bench_fq_mul_assign(b: &mut Bencher) { 87 | const SAMPLES: usize = 1000; 88 | 89 | let mut rng = XorShiftRng::from_seed([ 90 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 91 | 0xe5, 92 | ]); 93 | 94 | let v: Vec<(Fq, Fq)> = (0..SAMPLES) 95 | .map(|_| (Fq::random(&mut rng), Fq::random(&mut rng))) 96 | .collect(); 97 | 98 | let mut count = 0; 99 | b.iter(|| { 100 | let mut tmp = v[count].0; 101 | tmp *= &v[count].1; 102 | count = (count + 1) % SAMPLES; 103 | tmp 104 | }); 105 | } 106 | 107 | fn bench_fq_square(b: &mut Bencher) { 108 | const SAMPLES: usize = 1000; 109 | 110 | let mut rng = XorShiftRng::from_seed([ 111 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 112 | 0xe5, 113 | ]); 114 | 115 | let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); 116 | 117 | let mut count = 0; 118 | b.iter(|| { 119 | let mut tmp = v[count]; 120 | tmp = tmp.square(); 121 | count = (count + 1) % SAMPLES; 122 | tmp 123 | }); 124 | } 125 | 126 | fn bench_fq_invert(b: &mut Bencher) { 127 | const SAMPLES: usize = 1000; 128 | 129 | let mut rng = XorShiftRng::from_seed([ 130 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 131 | 0xe5, 132 | ]); 133 | 134 | let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); 135 | 136 | let mut count = 0; 137 | b.iter(|| { 138 | count = (count + 1) % SAMPLES; 139 | v[count].invert() 140 | }); 141 | } 142 | 143 | fn bench_fq_neg(b: &mut Bencher) { 144 | const SAMPLES: usize = 1000; 145 | 146 | let mut rng = XorShiftRng::from_seed([ 147 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 148 | 0xe5, 149 | ]); 150 | 151 | let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); 152 | 153 | let mut count = 0; 154 | b.iter(|| { 155 | let mut tmp = v[count]; 156 | tmp = tmp.neg(); 157 | count = (count + 1) % SAMPLES; 158 | tmp 159 | }); 160 | } 161 | 162 | fn bench_fq_sqrt(b: &mut Bencher) { 163 | const SAMPLES: usize = 1000; 164 | 165 | let mut rng = XorShiftRng::from_seed([ 166 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 167 | 0xe5, 168 | ]); 169 | 170 | let v: Vec = (0..SAMPLES) 171 | .map(|_| { 172 | let tmp = Fq::random(&mut rng); 173 | tmp.square() 174 | }) 175 | .collect(); 176 | 177 | let mut count = 0; 178 | b.iter(|| { 179 | count = (count + 1) % SAMPLES; 180 | v[count].sqrt() 181 | }); 182 | } 183 | 184 | fn bench_fq_to_repr(b: &mut Bencher) { 185 | const SAMPLES: usize = 1000; 186 | 187 | let mut rng = XorShiftRng::from_seed([ 188 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 189 | 0xe5, 190 | ]); 191 | 192 | let v: Vec = (0..SAMPLES).map(|_| Fq::random(&mut rng)).collect(); 193 | 194 | let mut count = 0; 195 | b.iter(|| { 196 | count = (count + 1) % SAMPLES; 197 | v[count].to_repr() 198 | }); 199 | } 200 | 201 | fn bench_fq_from_repr(b: &mut Bencher) { 202 | const SAMPLES: usize = 1000; 203 | 204 | let mut rng = XorShiftRng::from_seed([ 205 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 0xbc, 206 | 0xe5, 207 | ]); 208 | 209 | let v: Vec<::Repr> = (0..SAMPLES) 210 | .map(|_| Fq::random(&mut rng).to_repr()) 211 | .collect(); 212 | 213 | let mut count = 0; 214 | b.iter(|| { 215 | count = (count + 1) % SAMPLES; 216 | Fq::from_repr(v[count]) 217 | }); 218 | } 219 | 220 | criterion_group!(benches, criterion_benchmark); 221 | criterion_main!(benches); 222 | -------------------------------------------------------------------------------- /benches/hashtocurve.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for hashing to the Pasta curves. 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | 5 | use pasta_curves::arithmetic::CurveExt; 6 | use pasta_curves::{pallas, vesta}; 7 | 8 | fn criterion_benchmark(c: &mut Criterion) { 9 | bench_hash_to_curve(c); 10 | } 11 | 12 | fn bench_hash_to_curve(c: &mut Criterion) { 13 | let mut group = c.benchmark_group("hash-to-curve"); 14 | 15 | let hash_pallas = pallas::Point::hash_to_curve("z.cash:test"); 16 | group.bench_function("Pallas", |b| b.iter(|| hash_pallas(b"benchmark"))); 17 | 18 | let hash_vesta = vesta::Point::hash_to_curve("z.cash:test"); 19 | group.bench_function("Vesta", |b| b.iter(|| hash_vesta(b"benchmark"))); 20 | } 21 | 22 | criterion_group!(benches, criterion_benchmark); 23 | criterion_main!(benches); 24 | -------------------------------------------------------------------------------- /benches/point.rs: -------------------------------------------------------------------------------- 1 | //! Benchmarks for point operations. 2 | 3 | use criterion::{criterion_group, criterion_main, Criterion}; 4 | 5 | use pasta_curves::arithmetic::CurveExt; 6 | use pasta_curves::{pallas, vesta}; 7 | 8 | fn criterion_benchmark(c: &mut Criterion) { 9 | point_bench::(c, "Pallas"); 10 | point_bench::(c, "Vesta"); 11 | } 12 | 13 | fn point_bench(c: &mut Criterion, name: &str) { 14 | let mut group = c.benchmark_group(name); 15 | 16 | let a = C::generator(); 17 | let b = a.double(); 18 | 19 | group.bench_function("point doubling", |bencher| bencher.iter(|| a.double())); 20 | 21 | group.bench_function("point addition", |bencher| bencher.iter(|| a + b)); 22 | 23 | group.bench_function("point subtraction", |bencher| bencher.iter(|| a - b)); 24 | 25 | group.bench_function("point to_bytes", |bencher| bencher.iter(|| a.to_bytes())); 26 | 27 | let repr = a.to_bytes(); 28 | group.bench_function("point from_bytes", |bencher| { 29 | bencher.iter(|| C::from_bytes(&repr)) 30 | }); 31 | 32 | group.bench_function("point to_affine", |bencher| bencher.iter(|| a.to_affine())); 33 | 34 | for &n in [100, 1000, 10000].iter() { 35 | let input = vec![a; n]; 36 | let mut output = vec![C::AffineRepr::default(); n]; 37 | group.bench_function(format!("point batch_normalize/{}", n), |bencher| { 38 | bencher.iter(|| C::batch_normalize(input.as_slice(), output.as_mut_slice())); 39 | }); 40 | } 41 | } 42 | 43 | criterion_group!(benches, criterion_benchmark); 44 | criterion_main!(benches); 45 | -------------------------------------------------------------------------------- /book/.gitignore: -------------------------------------------------------------------------------- 1 | book 2 | -------------------------------------------------------------------------------- /book/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | all: 3 | find src -type f -a -name '*.md' |sed 's/[.]md$$/.html/g' |xargs $(MAKE) 4 | 5 | clean: 6 | find src -type f -a -name '*.html' -print0 |xargs -0 rm 7 | 8 | %.html: %.md 9 | pandoc --katex --from=markdown --to=html "$<" "--output=$@" 10 | ./edithtml.sh "$@" "$<" 11 | -------------------------------------------------------------------------------- /book/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = [ 3 | "Jack Grigg", 4 | "Sean Bowe", 5 | "Daira Hopwood", 6 | "Ying Tong Lai", 7 | ] 8 | language = "en" 9 | multilingual = false 10 | src = "src" 11 | title = "The Pasta Book" 12 | 13 | [preprocessor.katex] 14 | -------------------------------------------------------------------------------- /book/edithtml.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cat - "$1" > "$1.prefix" < 5 | 6 | 7 | 8 | 9 | 10 | $2 11 | 17 | 18 | 19 | 21 | 22 | 23 | EOF 24 | cat "$1.prefix" - >"$1" < 26 | 27 | EOF 28 | rm -f "$1.prefix" 29 | -------------------------------------------------------------------------------- /book/src/README.md: -------------------------------------------------------------------------------- 1 | {{#include ../../README.md}} 2 | -------------------------------------------------------------------------------- /book/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # The Pasta Book 2 | 3 | [Pasta](README.md) 4 | - [Design](design.md) 5 | - [Implementation](design/implementation.md) 6 | - [Fields](design/implementation/fields.md) 7 | -------------------------------------------------------------------------------- /book/src/design.md: -------------------------------------------------------------------------------- 1 | # Design 2 | 3 | ## Note on Language 4 | 5 | We use slightly different language than others to describe PLONK concepts. Here's the 6 | overview: 7 | 8 | 1. We like to think of PLONK-like arguments as tables, where each column corresponds to a 9 | "wire". We refer to entries in this table as "cells". 10 | 2. We like to call "selector polynomials" and so on "fixed columns" instead. We then refer 11 | specifically to a "selector constraint" when a cell in a fixed column is being used to 12 | control whether a particular constraint is enabled in that row. 13 | 3. We call the other polynomials "advice columns" usually, when they're populated by the 14 | prover. 15 | 4. We use the term "rule" to refer to a "gate" like 16 | $$A(X) \cdot q_A(X) + B(X) \cdot q_B(X) + A(X) \cdot B(X) \cdot q_M(X) + C(X) \cdot q_C(X) = 0.$$ 17 | - TODO: Check how consistent we are with this, and update the code and docs to match. 18 | -------------------------------------------------------------------------------- /book/src/design/implementation.md: -------------------------------------------------------------------------------- 1 | # Implementation 2 | -------------------------------------------------------------------------------- /book/src/design/implementation/fields.md: -------------------------------------------------------------------------------- 1 | # Fields 2 | 3 | The [Pasta curves](https://electriccoin.co/blog/the-pasta-curves-for-halo-2-and-beyond/) 4 | are designed to be highly 2-adic, meaning that a large $2^S$ 5 | [multiplicative subgroup](https://zcash.github.io/halo2/background/fields.html#multiplicative-subgroups) 6 | exists in each field. That is, we can write $p - 1 \equiv 2^S \cdot T$ with $T$ odd. For 7 | both Pallas and Vesta, $S = 32$; this helps to simplify the field implementations. 8 | 9 | ## Sarkar square-root algorithm (table-based variant) 10 | 11 | We use a technique from [Sarkar2020](https://eprint.iacr.org/2020/1407.pdf) to compute 12 | [square roots](https://zcash.github.io/halo2/background/fields.html#square-roots) in 13 | `pasta_curves`. The intuition 14 | behind the algorithm is that we can split the task into computing square roots in each 15 | multiplicative subgroup. 16 | 17 | Suppose we want to find the square root of $u$ modulo one of the Pasta primes $p$, where 18 | $u$ is a non-zero square in $\mathbb{Z}_p^\times$. We define a $2^S$ 19 | [root of unity](https://zcash.github.io/halo2/background/fields.html#roots-of-unity) 20 | $g = z^T$ where $z$ is a non-square in $\mathbb{Z}_p^\times$, and precompute the following 21 | tables: 22 | 23 | $$ 24 | gtab = \begin{bmatrix} 25 | g^0 & g^1 & ... & g^{2^8 - 1} \\ 26 | (g^{2^8})^0 & (g^{2^8})^1 & ... & (g^{2^8})^{2^8 - 1} \\ 27 | (g^{2^{16}})^0 & (g^{2^{16}})^1 & ... & (g^{2^{16}})^{2^8 - 1} \\ 28 | (g^{2^{24}})^0 & (g^{2^{24}})^1 & ... & (g^{2^{24}})^{2^8 - 1} 29 | \end{bmatrix} 30 | $$ 31 | 32 | $$ 33 | invtab = \begin{bmatrix} 34 | (g^{2^{-24}})^0 & (g^{2^{-24}})^1 & ... & (g^{2^{-24}})^{2^8 - 1} 35 | \end{bmatrix} 36 | $$ 37 | 38 | Let $v = u^{(T-1)/2}$. We can then define $x = uv \cdot v = u^T$ as an element of the 39 | $2^S$ multiplicative subgroup. 40 | 41 | Let $x_3 = x, x_2 = x_3^{2^8}, x_1 = x_2^{2^8}, x_0 = x_1^{2^8}.$ 42 | 43 | ### i = 0, 1 44 | Using $invtab$, we lookup $t_0$ such that 45 | $$ 46 | x_0 = (g^{2^{-24}})^{t_0} \implies x_0 \cdot g^{t_0 \cdot 2^{24}} = 1. 47 | $$ 48 | 49 | Define $\alpha_1 = x_1 \cdot (g^{2^{16}})^{t_0}.$ 50 | 51 | ### i = 2 52 | Lookup $t_1$ s.t. 53 | $$ 54 | \begin{array}{ll} 55 | \alpha_1 = (g^{2^{-24}})^{t_1} &\implies x_1 \cdot (g^{2^{16}})^{t_0} = (g^{2^{-24}})^{t_1} \\ 56 | &\implies 57 | x_1 \cdot g^{(t_0 + 2^8 \cdot t_1) \cdot 2^{16}} = 1. 58 | \end{array} 59 | $$ 60 | 61 | Define $\alpha_2 = x_2 \cdot (g^{2^8})^{t_0 + 2^8 \cdot t_1}.$ 62 | 63 | ### i = 3 64 | Lookup $t_2$ s.t. 65 | 66 | $$ 67 | \begin{array}{ll} 68 | \alpha_2 = (g^{2^{-24}})^{t_2} &\implies x_2 \cdot (g^{2^8})^{t_0 + 2^8\cdot {t_1}} = (g^{2^{-24}})^{t_2} \\ 69 | &\implies x_2 \cdot g^{(t_0 + 2^8 \cdot t_1 + 2^{16} \cdot t_2) \cdot 2^8} = 1. 70 | \end{array} 71 | $$ 72 | 73 | Define $\alpha_3 = x_3 \cdot g^{t_0 + 2^8 \cdot t_1 + 2^{16} \cdot t_2}.$ 74 | 75 | ### Final result 76 | Lookup $t_3$ such that 77 | 78 | $$ 79 | \begin{array}{ll} 80 | \alpha_3 = (g^{2^{-24}})^{t_3} &\implies x_3 \cdot g^{t_0 + 2^8\cdot {t_1} + 2^{16} \cdot t_2} = (g^{2^{-24}})^{t_3} \\ 81 | &\implies x_3 \cdot g^{t_0 + 2^8 \cdot t_1 + 2^{16} \cdot t_2 + 2^{24} \cdot t_3} = 1. 82 | \end{array} 83 | $$ 84 | 85 | Let $t = t_0 + 2^8 \cdot t_1 + 2^{16} \cdot t_2 + 2^{24} \cdot t_3$. 86 | 87 | We can now write 88 | $$ 89 | \begin{array}{lclcl} 90 | x_3 \cdot g^{t} = 1 &\implies& x_3 &=& g^{-t} \\ 91 | &\implies& uv^2 &=& g^{-t} \\ 92 | &\implies& uv &=& v^{-1} \cdot g^{-t} \\ 93 | &\implies& uv \cdot g^{t / 2} &=& v^{-1} \cdot g^{-t / 2}. 94 | \end{array} 95 | $$ 96 | 97 | Squaring the RHS, we observe that $(v^{-1} g^{-t / 2})^2 = v^{-2}g^{-t} = u.$ Therefore, 98 | the square root of $u$ is $uv \cdot g^{t / 2}$; the first part we computed earlier, and 99 | the second part can be computed with three multiplications using lookups in $gtab$. 100 | -------------------------------------------------------------------------------- /katex-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.63.0" 3 | components = [ "clippy", "rustfmt" ] 4 | -------------------------------------------------------------------------------- /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 | mod curves; 8 | mod fields; 9 | 10 | pub use curves::*; 11 | pub(crate) use fields::*; 12 | -------------------------------------------------------------------------------- /src/arithmetic/curves.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the `Curve`/`CurveAffine` abstractions that allow us to 2 | //! write code that generalizes over a pair of groups. 3 | 4 | #[cfg(feature = "alloc")] 5 | use group::prime::{PrimeCurve, PrimeCurveAffine}; 6 | #[cfg(feature = "alloc")] 7 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 8 | 9 | #[cfg(feature = "alloc")] 10 | use alloc::boxed::Box; 11 | #[cfg(feature = "alloc")] 12 | use core::ops::{Add, Mul, Sub}; 13 | 14 | /// This trait is a common interface for dealing with elements of an elliptic 15 | /// curve group in a "projective" form, where that arithmetic is usually more 16 | /// efficient. 17 | /// 18 | /// Requires the `alloc` feature flag because of `hash_to_curve`. 19 | #[cfg(feature = "alloc")] 20 | #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] 21 | pub trait CurveExt: 22 | PrimeCurve::AffineExt> 23 | + group::Group::ScalarExt> 24 | + Default 25 | + ConditionallySelectable 26 | + ConstantTimeEq 27 | + From<::Affine> 28 | { 29 | /// The scalar field of this elliptic curve. 30 | type ScalarExt: ff::WithSmallOrderMulGroup<3>; 31 | /// The base field over which this elliptic curve is constructed. 32 | type Base: ff::WithSmallOrderMulGroup<3>; 33 | /// The affine version of the curve 34 | type AffineExt: CurveAffine::ScalarExt> 35 | + Mul 36 | + for<'r> Mul; 37 | 38 | /// CURVE_ID used for hash-to-curve. 39 | const CURVE_ID: &'static str; 40 | 41 | /// Apply the curve endomorphism by multiplying the x-coordinate 42 | /// by an element of multiplicative order 3. 43 | fn endo(&self) -> Self; 44 | 45 | /// Return the Jacobian coordinates of this point. 46 | fn jacobian_coordinates(&self) -> (Self::Base, Self::Base, Self::Base); 47 | 48 | /// Requests a hasher that accepts messages and returns near-uniformly 49 | /// distributed elements in the group, given domain prefix `domain_prefix`. 50 | /// 51 | /// This method is suitable for use as a random oracle. 52 | /// 53 | /// # Example 54 | /// 55 | /// ``` 56 | /// use pasta_curves::arithmetic::CurveExt; 57 | /// fn pedersen_commitment( 58 | /// x: C::ScalarExt, 59 | /// r: C::ScalarExt, 60 | /// ) -> C::Affine { 61 | /// let hasher = C::hash_to_curve("z.cash:example_pedersen_commitment"); 62 | /// let g = hasher(b"g"); 63 | /// let h = hasher(b"h"); 64 | /// (g * x + &(h * r)).to_affine() 65 | /// } 66 | /// ``` 67 | #[allow(clippy::type_complexity)] 68 | fn hash_to_curve<'a>(domain_prefix: &'a str) -> Box Self + 'a>; 69 | 70 | /// Returns whether or not this element is on the curve; should 71 | /// always be true unless an "unchecked" API was used. 72 | fn is_on_curve(&self) -> Choice; 73 | 74 | /// Returns the curve constant a. 75 | fn a() -> Self::Base; 76 | 77 | /// Returns the curve constant b. 78 | fn b() -> Self::Base; 79 | 80 | /// Obtains a point given Jacobian coordinates $X : Y : Z$, failing 81 | /// if the coordinates are not on the curve. 82 | fn new_jacobian(x: Self::Base, y: Self::Base, z: Self::Base) -> CtOption; 83 | } 84 | 85 | /// This trait is the affine counterpart to `Curve` and is used for 86 | /// serialization, storage in memory, and inspection of $x$ and $y$ coordinates. 87 | /// 88 | /// Requires the `alloc` feature flag because of `hash_to_curve` on [`CurveExt`]. 89 | #[cfg(feature = "alloc")] 90 | #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] 91 | pub trait CurveAffine: 92 | PrimeCurveAffine< 93 | Scalar = ::ScalarExt, 94 | Curve = ::CurveExt, 95 | > + Default 96 | + Add::Curve> 97 | + Sub::Curve> 98 | + ConditionallySelectable 99 | + ConstantTimeEq 100 | + From<::Curve> 101 | { 102 | /// The scalar field of this elliptic curve. 103 | type ScalarExt: ff::WithSmallOrderMulGroup<3> + Ord; 104 | /// The base field over which this elliptic curve is constructed. 105 | type Base: ff::WithSmallOrderMulGroup<3> + Ord; 106 | /// The projective form of the curve 107 | type CurveExt: CurveExt::ScalarExt>; 108 | 109 | /// Gets the coordinates of this point. 110 | /// 111 | /// Returns None if this is the identity. 112 | fn coordinates(&self) -> CtOption>; 113 | 114 | /// Obtains a point given $(x, y)$, failing if it is not on the 115 | /// curve. 116 | fn from_xy(x: Self::Base, y: Self::Base) -> CtOption; 117 | 118 | /// Returns whether or not this element is on the curve; should 119 | /// always be true unless an "unchecked" API was used. 120 | fn is_on_curve(&self) -> Choice; 121 | 122 | /// Returns the curve constant $a$. 123 | fn a() -> Self::Base; 124 | 125 | /// Returns the curve constant $b$. 126 | fn b() -> Self::Base; 127 | } 128 | 129 | /// The affine coordinates of a point on an elliptic curve. 130 | #[cfg(feature = "alloc")] 131 | #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] 132 | #[derive(Clone, Copy, Debug, Default)] 133 | pub struct Coordinates { 134 | pub(crate) x: C::Base, 135 | pub(crate) y: C::Base, 136 | } 137 | 138 | #[cfg(feature = "alloc")] 139 | impl Coordinates { 140 | /// Obtains a `Coordinates` value given $(x, y)$, failing if it is not on the curve. 141 | pub fn from_xy(x: C::Base, y: C::Base) -> CtOption { 142 | // We use CurveAffine::from_xy to validate the coordinates. 143 | C::from_xy(x, y).map(|_| Coordinates { x, y }) 144 | } 145 | /// Returns the x-coordinate. 146 | /// 147 | /// Equivalent to `Coordinates::u`. 148 | pub fn x(&self) -> &C::Base { 149 | &self.x 150 | } 151 | 152 | /// Returns the y-coordinate. 153 | /// 154 | /// Equivalent to `Coordinates::v`. 155 | pub fn y(&self) -> &C::Base { 156 | &self.y 157 | } 158 | 159 | /// Returns the u-coordinate. 160 | /// 161 | /// Equivalent to `Coordinates::x`. 162 | pub fn u(&self) -> &C::Base { 163 | &self.x 164 | } 165 | 166 | /// Returns the v-coordinate. 167 | /// 168 | /// Equivalent to `Coordinates::y`. 169 | pub fn v(&self) -> &C::Base { 170 | &self.y 171 | } 172 | } 173 | 174 | #[cfg(feature = "alloc")] 175 | impl ConditionallySelectable for Coordinates { 176 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 177 | Coordinates { 178 | x: C::Base::conditional_select(&a.x, &b.x, choice), 179 | y: C::Base::conditional_select(&a.y, &b.y, choice), 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/arithmetic/fields.rs: -------------------------------------------------------------------------------- 1 | //! This module contains the `Field` abstraction that allows us to write 2 | //! code that generalizes over a pair of fields. 3 | 4 | use core::mem::size_of; 5 | 6 | use static_assertions::const_assert; 7 | 8 | #[cfg(feature = "sqrt-table")] 9 | use alloc::{boxed::Box, vec::Vec}; 10 | #[cfg(feature = "sqrt-table")] 11 | use core::marker::PhantomData; 12 | 13 | #[cfg(feature = "sqrt-table")] 14 | use subtle::Choice; 15 | 16 | const_assert!(size_of::() >= 4); 17 | 18 | /// An internal trait that exposes additional operations related to calculating square roots of 19 | /// prime-order finite fields. 20 | pub(crate) trait SqrtTableHelpers: ff::PrimeField { 21 | /// Raise this field element to the power $(t-1)/2$. 22 | /// 23 | /// Field implementations may override this to use an efficient addition chain. 24 | fn pow_by_t_minus1_over2(&self) -> Self; 25 | 26 | /// Gets the lower 32 bits of this field element when expressed 27 | /// canonically. 28 | fn get_lower_32(&self) -> u32; 29 | } 30 | 31 | /// Parameters for a perfect hash function used in square root computation. 32 | #[cfg(feature = "sqrt-table")] 33 | #[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))] 34 | #[derive(Debug)] 35 | struct SqrtHasher { 36 | hash_xor: u32, 37 | hash_mod: usize, 38 | marker: PhantomData, 39 | } 40 | 41 | #[cfg(feature = "sqrt-table")] 42 | impl SqrtHasher { 43 | /// Returns a perfect hash of x for use with SqrtTables::inv. 44 | fn hash(&self, x: &F) -> usize { 45 | // This is just the simplest constant-time perfect hash construction that could 46 | // possibly work. The 32 low-order bits are unique within the 2^S order subgroup, 47 | // then the xor acts as "salt" to injectively randomize the output when taken modulo 48 | // `hash_mod`. Since the table is small, we do not need anything more complicated. 49 | ((x.get_lower_32() ^ self.hash_xor) as usize) % self.hash_mod 50 | } 51 | } 52 | 53 | /// Tables used for square root computation. 54 | #[cfg(feature = "sqrt-table")] 55 | #[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))] 56 | #[derive(Debug)] 57 | pub(crate) struct SqrtTables { 58 | hasher: SqrtHasher, 59 | inv: Vec, 60 | g0: Box<[F; 256]>, 61 | g1: Box<[F; 256]>, 62 | g2: Box<[F; 256]>, 63 | g3: Box<[F; 129]>, 64 | } 65 | 66 | #[cfg(feature = "sqrt-table")] 67 | impl SqrtTables { 68 | /// Build tables given parameters for the perfect hash. 69 | pub fn new(hash_xor: u32, hash_mod: usize) -> Self { 70 | use alloc::vec; 71 | 72 | let hasher = SqrtHasher { 73 | hash_xor, 74 | hash_mod, 75 | marker: PhantomData, 76 | }; 77 | 78 | let mut gtab = (0..4).scan(F::ROOT_OF_UNITY, |gi, _| { 79 | // gi == ROOT_OF_UNITY^(256^i) 80 | let gtab_i: Vec = (0..256) 81 | .scan(F::ONE, |acc, _| { 82 | let res = *acc; 83 | *acc *= *gi; 84 | Some(res) 85 | }) 86 | .collect(); 87 | *gi = gtab_i[255] * *gi; 88 | Some(gtab_i) 89 | }); 90 | let gtab_0 = gtab.next().unwrap(); 91 | let gtab_1 = gtab.next().unwrap(); 92 | let gtab_2 = gtab.next().unwrap(); 93 | let mut gtab_3 = gtab.next().unwrap(); 94 | assert_eq!(gtab.next(), None); 95 | 96 | // Now invert gtab[3]. 97 | let mut inv: Vec = vec![1; hash_mod]; 98 | for (j, gtab_3_j) in gtab_3.iter().enumerate() { 99 | let hash = hasher.hash(gtab_3_j); 100 | // 1 is the last value to be assigned, so this ensures there are no collisions. 101 | assert!(inv[hash] == 1); 102 | inv[hash] = ((256 - j) & 0xFF) as u8; 103 | } 104 | 105 | gtab_3.truncate(129); 106 | 107 | SqrtTables:: { 108 | hasher, 109 | inv, 110 | g0: gtab_0.into_boxed_slice().try_into().unwrap(), 111 | g1: gtab_1.into_boxed_slice().try_into().unwrap(), 112 | g2: gtab_2.into_boxed_slice().try_into().unwrap(), 113 | g3: gtab_3.into_boxed_slice().try_into().unwrap(), 114 | } 115 | } 116 | 117 | /// Computes: 118 | /// 119 | /// * (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field; 120 | /// * (true, 0), if num is zero; 121 | /// * (false, 0), if num is nonzero and div is zero; 122 | /// * (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field; 123 | /// 124 | /// where ROOT_OF_UNITY is a generator of the order 2^n subgroup (and therefore a nonsquare). 125 | /// 126 | /// The choice of root from sqrt is unspecified. 127 | pub fn sqrt_ratio(&self, num: &F, div: &F) -> (Choice, F) { 128 | // Based on: 129 | // * [Sarkar2020](https://eprint.iacr.org/2020/1407) 130 | // * [BDLSY2012](https://cr.yp.to/papers.html#ed25519) 131 | // 132 | // We need to calculate uv and v, where v = u^((T-1)/2), u = num/div, and p-1 = T * 2^S. 133 | // We can rewrite as follows: 134 | // 135 | // v = (num/div)^((T-1)/2) 136 | // = num^((T-1)/2) * div^(p-1 - (T-1)/2) [Fermat's Little Theorem] 137 | // = " * div^(T * 2^S - (T-1)/2) 138 | // = " * div^((2^(S+1) - 1)*(T-1)/2 + 2^S) 139 | // = (num * div^(2^(S+1) - 1))^((T-1)/2) * div^(2^S) 140 | // 141 | // Let w = (num * div^(2^(S+1) - 1))^((T-1)/2) * div^(2^S - 1). 142 | // Then v = w * div, and uv = num * v / div = num * w. 143 | // 144 | // We calculate: 145 | // 146 | // s = div^(2^S - 1) using an addition chain 147 | // t = div^(2^(S+1) - 1) = s^2 * div 148 | // w = (num * t)^((T-1)/2) * s using another addition chain 149 | // 150 | // then u and uv as above. The addition chains are given in 151 | // https://github.com/zcash/pasta/blob/master/addchain_sqrt.py . 152 | // The overall cost of this part is similar to a single full-width exponentiation, 153 | // regardless of S. 154 | 155 | let sqr = |x: F, i: u32| (0..i).fold(x, |x, _| x.square()); 156 | 157 | // s = div^(2^S - 1) 158 | let s = (0..5).fold(*div, |d: F, i| sqr(d, 1 << i) * d); 159 | 160 | // t == div^(2^(S+1) - 1) 161 | let t = s.square() * div; 162 | 163 | // w = (num * t)^((T-1)/2) * s 164 | let w = (t * num).pow_by_t_minus1_over2() * s; 165 | 166 | // v == u^((T-1)/2) 167 | let v = w * div; 168 | 169 | // uv = u * v 170 | let uv = w * num; 171 | 172 | let res = self.sqrt_common(&uv, &v); 173 | 174 | let sqdiv = res.square() * div; 175 | let is_square = (sqdiv - num).is_zero(); 176 | let is_nonsquare = (sqdiv - F::ROOT_OF_UNITY * num).is_zero(); 177 | assert!(bool::from( 178 | num.is_zero() | div.is_zero() | (is_square ^ is_nonsquare) 179 | )); 180 | 181 | (is_square, res) 182 | } 183 | 184 | /// Same as sqrt_ratio(u, one()) but more efficient. 185 | pub fn sqrt_alt(&self, u: &F) -> (Choice, F) { 186 | let v = u.pow_by_t_minus1_over2(); 187 | let uv = *u * v; 188 | 189 | let res = self.sqrt_common(&uv, &v); 190 | 191 | let sq = res.square(); 192 | let is_square = (sq - u).is_zero(); 193 | let is_nonsquare = (sq - F::ROOT_OF_UNITY * u).is_zero(); 194 | assert!(bool::from(u.is_zero() | (is_square ^ is_nonsquare))); 195 | 196 | (is_square, res) 197 | } 198 | 199 | /// Common part of sqrt_ratio and sqrt_alt: return their result given v = u^((T-1)/2) and uv = u * v. 200 | fn sqrt_common(&self, uv: &F, v: &F) -> F { 201 | let sqr = |x: F, i: u32| (0..i).fold(x, |x, _| x.square()); 202 | let inv = |x: F| self.inv[self.hasher.hash(&x)] as usize; 203 | 204 | let x3 = *uv * v; 205 | let x2 = sqr(x3, 8); 206 | let x1 = sqr(x2, 8); 207 | let x0 = sqr(x1, 8); 208 | 209 | // i = 0, 1 210 | let mut t_ = inv(x0); // = t >> 16 211 | // 1 == x0 * ROOT_OF_UNITY^(t_ << 24) 212 | assert!(t_ < 0x100); 213 | let alpha = x1 * self.g2[t_]; 214 | 215 | // i = 2 216 | t_ += inv(alpha) << 8; // = t >> 8 217 | // 1 == x1 * ROOT_OF_UNITY^(t_ << 16) 218 | assert!(t_ < 0x10000); 219 | let alpha = x2 * self.g1[t_ & 0xFF] * self.g2[t_ >> 8]; 220 | 221 | // i = 3 222 | t_ += inv(alpha) << 16; // = t 223 | // 1 == x2 * ROOT_OF_UNITY^(t_ << 8) 224 | assert!(t_ < 0x1000000); 225 | let alpha = x3 * self.g0[t_ & 0xFF] * self.g1[(t_ >> 8) & 0xFF] * self.g2[t_ >> 16]; 226 | 227 | t_ += inv(alpha) << 24; // = t << 1 228 | // 1 == x3 * ROOT_OF_UNITY^t_ 229 | t_ = (((t_ as u64) + 1) >> 1) as usize; 230 | assert!(t_ <= 0x80000000); 231 | 232 | *uv * self.g0[t_ & 0xFF] 233 | * self.g1[(t_ >> 8) & 0xFF] 234 | * self.g2[(t_ >> 16) & 0xFF] 235 | * self.g3[t_ >> 24] 236 | } 237 | } 238 | 239 | /// Compute a + b + carry, returning the result and the new carry over. 240 | #[inline(always)] 241 | pub(crate) const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { 242 | let ret = (a as u128) + (b as u128) + (carry as u128); 243 | (ret as u64, (ret >> 64) as u64) 244 | } 245 | 246 | /// Compute a - (b + borrow), returning the result and the new borrow. 247 | #[inline(always)] 248 | pub(crate) const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { 249 | let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); 250 | (ret as u64, (ret >> 64) as u64) 251 | } 252 | 253 | /// Compute a + (b * c) + carry, returning the result and the new carry over. 254 | #[inline(always)] 255 | pub(crate) const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { 256 | let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); 257 | (ret as u64, (ret >> 64) as u64) 258 | } 259 | -------------------------------------------------------------------------------- /src/fields.rs: -------------------------------------------------------------------------------- 1 | //! This module contains implementations for the two finite fields of the Pallas 2 | //! and Vesta curves. 3 | 4 | mod fp; 5 | mod fq; 6 | 7 | pub use fp::*; 8 | pub use fq::*; 9 | 10 | /// Converts 64-bit little-endian limbs to 32-bit little endian limbs. 11 | #[cfg(feature = "gpu")] 12 | fn u64_to_u32(limbs: &[u64]) -> alloc::vec::Vec { 13 | limbs 14 | .iter() 15 | .flat_map(|limb| [(limb & 0xFFFF_FFFF) as u32, (limb >> 32) as u32].into_iter()) 16 | .collect() 17 | } 18 | 19 | #[cfg(feature = "gpu")] 20 | #[test] 21 | fn test_u64_to_u32() { 22 | use rand::{RngCore, SeedableRng}; 23 | use rand_xorshift::XorShiftRng; 24 | 25 | let mut rng = XorShiftRng::from_seed([0; 16]); 26 | let u64_limbs: alloc::vec::Vec = (0..6).map(|_| rng.next_u64()).collect(); 27 | let u32_limbs = crate::fields::u64_to_u32(&u64_limbs); 28 | 29 | let u64_le_bytes: alloc::vec::Vec = u64_limbs 30 | .iter() 31 | .flat_map(|limb| limb.to_le_bytes()) 32 | .collect(); 33 | let u32_le_bytes: alloc::vec::Vec = u32_limbs 34 | .iter() 35 | .flat_map(|limb| limb.to_le_bytes()) 36 | .collect(); 37 | 38 | assert_eq!(u64_le_bytes, u32_le_bytes); 39 | } 40 | -------------------------------------------------------------------------------- /src/fields/fp.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::ops::{Add, Mul, Neg, Sub}; 3 | 4 | use ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; 5 | use rand::RngCore; 6 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 7 | 8 | #[cfg(feature = "sqrt-table")] 9 | use lazy_static::lazy_static; 10 | 11 | #[cfg(feature = "bits")] 12 | use ff::{FieldBits, PrimeFieldBits}; 13 | 14 | use crate::arithmetic::{adc, mac, sbb, SqrtTableHelpers}; 15 | 16 | #[cfg(feature = "sqrt-table")] 17 | use crate::arithmetic::SqrtTables; 18 | 19 | /// This represents an element of $\mathbb{F}_p$ where 20 | /// 21 | /// `p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001` 22 | /// 23 | /// is the base field of the Pallas curve. 24 | // The internal representation of this type is four 64-bit unsigned 25 | // integers in little-endian order. `Fp` values are always in 26 | // Montgomery form; i.e., Fp(a) = aR mod p, with R = 2^256. 27 | #[derive(Clone, Copy, Eq)] 28 | #[repr(transparent)] 29 | pub struct Fp(pub(crate) [u64; 4]); 30 | 31 | impl fmt::Debug for Fp { 32 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 33 | let tmp = self.to_repr(); 34 | write!(f, "0x")?; 35 | for &b in tmp.iter().rev() { 36 | write!(f, "{:02x}", b)?; 37 | } 38 | Ok(()) 39 | } 40 | } 41 | 42 | impl From for Fp { 43 | fn from(bit: bool) -> Fp { 44 | if bit { 45 | Fp::one() 46 | } else { 47 | Fp::zero() 48 | } 49 | } 50 | } 51 | 52 | impl From for Fp { 53 | fn from(val: u64) -> Fp { 54 | Fp([val, 0, 0, 0]) * R2 55 | } 56 | } 57 | 58 | impl ConstantTimeEq for Fp { 59 | fn ct_eq(&self, other: &Self) -> Choice { 60 | self.0[0].ct_eq(&other.0[0]) 61 | & self.0[1].ct_eq(&other.0[1]) 62 | & self.0[2].ct_eq(&other.0[2]) 63 | & self.0[3].ct_eq(&other.0[3]) 64 | } 65 | } 66 | 67 | impl PartialEq for Fp { 68 | #[inline] 69 | fn eq(&self, other: &Self) -> bool { 70 | self.ct_eq(other).unwrap_u8() == 1 71 | } 72 | } 73 | 74 | impl core::cmp::Ord for Fp { 75 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { 76 | let left = self.to_repr(); 77 | let right = other.to_repr(); 78 | left.iter() 79 | .zip(right.iter()) 80 | .rev() 81 | .find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) { 82 | core::cmp::Ordering::Equal => None, 83 | res => Some(res), 84 | }) 85 | .unwrap_or(core::cmp::Ordering::Equal) 86 | } 87 | } 88 | 89 | impl core::cmp::PartialOrd for Fp { 90 | fn partial_cmp(&self, other: &Self) -> Option { 91 | Some(self.cmp(other)) 92 | } 93 | } 94 | 95 | impl ConditionallySelectable for Fp { 96 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 97 | Fp([ 98 | u64::conditional_select(&a.0[0], &b.0[0], choice), 99 | u64::conditional_select(&a.0[1], &b.0[1], choice), 100 | u64::conditional_select(&a.0[2], &b.0[2], choice), 101 | u64::conditional_select(&a.0[3], &b.0[3], choice), 102 | ]) 103 | } 104 | } 105 | 106 | /// Constant representing the modulus 107 | /// p = 0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001 108 | const MODULUS: Fp = Fp([ 109 | 0x992d30ed00000001, 110 | 0x224698fc094cf91b, 111 | 0x0000000000000000, 112 | 0x4000000000000000, 113 | ]); 114 | 115 | /// The modulus as u32 limbs. 116 | #[cfg(not(target_pointer_width = "64"))] 117 | const MODULUS_LIMBS_32: [u32; 8] = [ 118 | 0x0000_0001, 119 | 0x992d_30ed, 120 | 0x094c_f91b, 121 | 0x2246_98fc, 122 | 0x0000_0000, 123 | 0x0000_0000, 124 | 0x0000_0000, 125 | 0x4000_0000, 126 | ]; 127 | 128 | impl<'a> Neg for &'a Fp { 129 | type Output = Fp; 130 | 131 | #[inline] 132 | fn neg(self) -> Fp { 133 | self.neg() 134 | } 135 | } 136 | 137 | impl Neg for Fp { 138 | type Output = Fp; 139 | 140 | #[inline] 141 | fn neg(self) -> Fp { 142 | -&self 143 | } 144 | } 145 | 146 | impl<'a, 'b> Sub<&'b Fp> for &'a Fp { 147 | type Output = Fp; 148 | 149 | #[inline] 150 | fn sub(self, rhs: &'b Fp) -> Fp { 151 | self.sub(rhs) 152 | } 153 | } 154 | 155 | impl<'a, 'b> Add<&'b Fp> for &'a Fp { 156 | type Output = Fp; 157 | 158 | #[inline] 159 | fn add(self, rhs: &'b Fp) -> Fp { 160 | self.add(rhs) 161 | } 162 | } 163 | 164 | impl<'a, 'b> Mul<&'b Fp> for &'a Fp { 165 | type Output = Fp; 166 | 167 | #[inline] 168 | fn mul(self, rhs: &'b Fp) -> Fp { 169 | self.mul(rhs) 170 | } 171 | } 172 | 173 | impl_binops_additive!(Fp, Fp); 174 | impl_binops_multiplicative!(Fp, Fp); 175 | 176 | impl> ::core::iter::Sum for Fp { 177 | fn sum>(iter: I) -> Self { 178 | iter.fold(Self::ZERO, |acc, item| acc + item.borrow()) 179 | } 180 | } 181 | 182 | impl> ::core::iter::Product for Fp { 183 | fn product>(iter: I) -> Self { 184 | iter.fold(Self::ONE, |acc, item| acc * item.borrow()) 185 | } 186 | } 187 | 188 | /// INV = -(p^{-1} mod 2^64) mod 2^64 189 | const INV: u64 = 0x992d30ecffffffff; 190 | 191 | /// R = 2^256 mod p 192 | const R: Fp = Fp([ 193 | 0x34786d38fffffffd, 194 | 0x992c350be41914ad, 195 | 0xffffffffffffffff, 196 | 0x3fffffffffffffff, 197 | ]); 198 | 199 | /// R^2 = 2^512 mod p 200 | const R2: Fp = Fp([ 201 | 0x8c78ecb30000000f, 202 | 0xd7d30dbd8b0de0e7, 203 | 0x7797a99bc3c95d18, 204 | 0x096d41af7b9cb714, 205 | ]); 206 | 207 | /// R^3 = 2^768 mod p 208 | const R3: Fp = Fp([ 209 | 0xf185a5993a9e10f9, 210 | 0xf6a68f3b6ac5b1d1, 211 | 0xdf8d1014353fd42c, 212 | 0x2ae309222d2d9910, 213 | ]); 214 | 215 | /// `GENERATOR = 5 mod p` is a generator of the `p - 1` order multiplicative 216 | /// subgroup, or in other words a primitive root of the field. 217 | const GENERATOR: Fp = Fp::from_raw([ 218 | 0x0000_0000_0000_0005, 219 | 0x0000_0000_0000_0000, 220 | 0x0000_0000_0000_0000, 221 | 0x0000_0000_0000_0000, 222 | ]); 223 | 224 | const S: u32 = 32; 225 | 226 | /// GENERATOR^t where t * 2^s + 1 = p 227 | /// with t odd. In other words, this 228 | /// is a 2^s root of unity. 229 | const ROOT_OF_UNITY: Fp = Fp::from_raw([ 230 | 0xbdad6fabd87ea32f, 231 | 0xea322bf2b7bb7584, 232 | 0x362120830561f81a, 233 | 0x2bce74deac30ebda, 234 | ]); 235 | 236 | /// GENERATOR^{2^s} where t * 2^s + 1 = p 237 | /// with t odd. In other words, this 238 | /// is a t root of unity. 239 | const DELTA: Fp = Fp::from_raw([ 240 | 0x6a6ccd20dd7b9ba2, 241 | 0xf5e4f3f13eee5636, 242 | 0xbd455b7112a5049d, 243 | 0x0a757d0f0006ab6c, 244 | ]); 245 | 246 | /// `(t - 1) // 2` where t * 2^s + 1 = p with t odd. 247 | #[cfg(any(test, not(feature = "sqrt-table")))] 248 | const T_MINUS1_OVER2: [u64; 4] = [ 249 | 0x04a6_7c8d_cc96_9876, 250 | 0x0000_0000_1123_4c7e, 251 | 0x0000_0000_0000_0000, 252 | 0x0000_0000_2000_0000, 253 | ]; 254 | 255 | impl Default for Fp { 256 | #[inline] 257 | fn default() -> Self { 258 | Self::zero() 259 | } 260 | } 261 | 262 | impl Fp { 263 | /// Returns zero, the additive identity. 264 | #[inline] 265 | pub const fn zero() -> Fp { 266 | Fp([0, 0, 0, 0]) 267 | } 268 | 269 | /// Returns one, the multiplicative identity. 270 | #[inline] 271 | pub const fn one() -> Fp { 272 | R 273 | } 274 | 275 | /// Doubles this field element. 276 | #[inline] 277 | pub const fn double(&self) -> Fp { 278 | // TODO: This can be achieved more efficiently with a bitshift. 279 | self.add(self) 280 | } 281 | 282 | fn from_u512(limbs: [u64; 8]) -> Fp { 283 | // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits 284 | // with the higher bits multiplied by 2^256. Thus, we perform two reductions 285 | // 286 | // 1. the lower bits are multiplied by R^2, as normal 287 | // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 288 | // 289 | // and computing their sum in the field. It remains to see that arbitrary 256-bit 290 | // numbers can be placed into Montgomery form safely using the reduction. The 291 | // reduction works so long as the product is less than R=2^256 multiplied by 292 | // the modulus. This holds because for any `c` smaller than the modulus, we have 293 | // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the 294 | // reduction always works so long as `c` is in the field; in this case it is either the 295 | // constant `R2` or `R3`. 296 | let d0 = Fp([limbs[0], limbs[1], limbs[2], limbs[3]]); 297 | let d1 = Fp([limbs[4], limbs[5], limbs[6], limbs[7]]); 298 | // Convert to Montgomery form 299 | d0 * R2 + d1 * R3 300 | } 301 | 302 | /// Converts from an integer represented in little endian 303 | /// into its (congruent) `Fp` representation. 304 | pub const fn from_raw(val: [u64; 4]) -> Self { 305 | (&Fp(val)).mul(&R2) 306 | } 307 | 308 | /// Squares this element. 309 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 310 | pub const fn square(&self) -> Fp { 311 | let (r1, carry) = mac(0, self.0[0], self.0[1], 0); 312 | let (r2, carry) = mac(0, self.0[0], self.0[2], carry); 313 | let (r3, r4) = mac(0, self.0[0], self.0[3], carry); 314 | 315 | let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); 316 | let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); 317 | 318 | let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); 319 | 320 | let r7 = r6 >> 63; 321 | let r6 = (r6 << 1) | (r5 >> 63); 322 | let r5 = (r5 << 1) | (r4 >> 63); 323 | let r4 = (r4 << 1) | (r3 >> 63); 324 | let r3 = (r3 << 1) | (r2 >> 63); 325 | let r2 = (r2 << 1) | (r1 >> 63); 326 | let r1 = r1 << 1; 327 | 328 | let (r0, carry) = mac(0, self.0[0], self.0[0], 0); 329 | let (r1, carry) = adc(0, r1, carry); 330 | let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); 331 | let (r3, carry) = adc(0, r3, carry); 332 | let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); 333 | let (r5, carry) = adc(0, r5, carry); 334 | let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); 335 | let (r7, _) = adc(0, r7, carry); 336 | 337 | Fp::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) 338 | } 339 | 340 | #[allow(clippy::too_many_arguments)] 341 | #[cfg_attr(not(feature = "uninline-portable"), inline(always))] 342 | const fn montgomery_reduce( 343 | r0: u64, 344 | r1: u64, 345 | r2: u64, 346 | r3: u64, 347 | r4: u64, 348 | r5: u64, 349 | r6: u64, 350 | r7: u64, 351 | ) -> Self { 352 | // The Montgomery reduction here is based on Algorithm 14.32 in 353 | // Handbook of Applied Cryptography 354 | // . 355 | 356 | let k = r0.wrapping_mul(INV); 357 | let (_, carry) = mac(r0, k, MODULUS.0[0], 0); 358 | let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); 359 | let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); 360 | let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); 361 | let (r4, carry2) = adc(r4, 0, carry); 362 | 363 | let k = r1.wrapping_mul(INV); 364 | let (_, carry) = mac(r1, k, MODULUS.0[0], 0); 365 | let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); 366 | let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); 367 | let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); 368 | let (r5, carry2) = adc(r5, carry2, carry); 369 | 370 | let k = r2.wrapping_mul(INV); 371 | let (_, carry) = mac(r2, k, MODULUS.0[0], 0); 372 | let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); 373 | let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); 374 | let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); 375 | let (r6, carry2) = adc(r6, carry2, carry); 376 | 377 | let k = r3.wrapping_mul(INV); 378 | let (_, carry) = mac(r3, k, MODULUS.0[0], 0); 379 | let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); 380 | let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); 381 | let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); 382 | let (r7, _) = adc(r7, carry2, carry); 383 | 384 | // Result may be within MODULUS of the correct value 385 | (&Fp([r4, r5, r6, r7])).sub(&MODULUS) 386 | } 387 | 388 | /// Multiplies `rhs` by `self`, returning the result. 389 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 390 | pub const fn mul(&self, rhs: &Self) -> Self { 391 | // Schoolbook multiplication 392 | 393 | let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); 394 | let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); 395 | let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); 396 | let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); 397 | 398 | let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); 399 | let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); 400 | let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); 401 | let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); 402 | 403 | let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); 404 | let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); 405 | let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); 406 | let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); 407 | 408 | let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); 409 | let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); 410 | let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); 411 | let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); 412 | 413 | Fp::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) 414 | } 415 | 416 | /// Subtracts `rhs` from `self`, returning the result. 417 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 418 | pub const fn sub(&self, rhs: &Self) -> Self { 419 | let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); 420 | let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); 421 | let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); 422 | let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); 423 | 424 | // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise 425 | // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. 426 | let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); 427 | let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); 428 | let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); 429 | let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); 430 | 431 | Fp([d0, d1, d2, d3]) 432 | } 433 | 434 | /// Adds `rhs` to `self`, returning the result. 435 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 436 | pub const fn add(&self, rhs: &Self) -> Self { 437 | let (d0, carry) = adc(self.0[0], rhs.0[0], 0); 438 | let (d1, carry) = adc(self.0[1], rhs.0[1], carry); 439 | let (d2, carry) = adc(self.0[2], rhs.0[2], carry); 440 | let (d3, _) = adc(self.0[3], rhs.0[3], carry); 441 | 442 | // Attempt to subtract the modulus, to ensure the value 443 | // is smaller than the modulus. 444 | (&Fp([d0, d1, d2, d3])).sub(&MODULUS) 445 | } 446 | 447 | /// Negates `self`. 448 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 449 | pub const fn neg(&self) -> Self { 450 | // Subtract `self` from `MODULUS` to negate. Ignore the final 451 | // borrow because it cannot underflow; self is guaranteed to 452 | // be in the field. 453 | let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); 454 | let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); 455 | let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); 456 | let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); 457 | 458 | // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is 459 | // zero if `self` was zero, and `u64::max_value()` if self was nonzero. 460 | let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); 461 | 462 | Fp([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) 463 | } 464 | } 465 | 466 | impl From for [u8; 32] { 467 | fn from(value: Fp) -> [u8; 32] { 468 | value.to_repr() 469 | } 470 | } 471 | 472 | impl<'a> From<&'a Fp> for [u8; 32] { 473 | fn from(value: &'a Fp) -> [u8; 32] { 474 | value.to_repr() 475 | } 476 | } 477 | 478 | impl ff::Field for Fp { 479 | const ZERO: Self = Self::zero(); 480 | const ONE: Self = Self::one(); 481 | 482 | fn random(mut rng: impl RngCore) -> Self { 483 | Self::from_u512([ 484 | rng.next_u64(), 485 | rng.next_u64(), 486 | rng.next_u64(), 487 | rng.next_u64(), 488 | rng.next_u64(), 489 | rng.next_u64(), 490 | rng.next_u64(), 491 | rng.next_u64(), 492 | ]) 493 | } 494 | 495 | fn double(&self) -> Self { 496 | self.double() 497 | } 498 | 499 | #[inline(always)] 500 | fn square(&self) -> Self { 501 | self.square() 502 | } 503 | 504 | fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { 505 | #[cfg(feature = "sqrt-table")] 506 | { 507 | FP_TABLES.sqrt_ratio(num, div) 508 | } 509 | 510 | #[cfg(not(feature = "sqrt-table"))] 511 | ff::helpers::sqrt_ratio_generic(num, div) 512 | } 513 | 514 | #[cfg(feature = "sqrt-table")] 515 | fn sqrt_alt(&self) -> (Choice, Self) { 516 | FP_TABLES.sqrt_alt(self) 517 | } 518 | 519 | /// Computes the square root of this element, if it exists. 520 | fn sqrt(&self) -> CtOption { 521 | #[cfg(feature = "sqrt-table")] 522 | { 523 | let (is_square, res) = FP_TABLES.sqrt_alt(self); 524 | CtOption::new(res, is_square) 525 | } 526 | 527 | #[cfg(not(feature = "sqrt-table"))] 528 | ff::helpers::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2) 529 | } 530 | 531 | /// Computes the multiplicative inverse of this element, 532 | /// failing if the element is zero. 533 | fn invert(&self) -> CtOption { 534 | let tmp = self.pow_vartime(&[ 535 | 0x992d30ecffffffff, 536 | 0x224698fc094cf91b, 537 | 0x0, 538 | 0x4000000000000000, 539 | ]); 540 | 541 | CtOption::new(tmp, !self.ct_eq(&Self::zero())) 542 | } 543 | 544 | fn pow_vartime>(&self, exp: S) -> Self { 545 | let mut res = Self::one(); 546 | let mut found_one = false; 547 | for e in exp.as_ref().iter().rev() { 548 | for i in (0..64).rev() { 549 | if found_one { 550 | res = res.square(); 551 | } 552 | 553 | if ((*e >> i) & 1) == 1 { 554 | found_one = true; 555 | res *= self; 556 | } 557 | } 558 | } 559 | res 560 | } 561 | } 562 | 563 | impl ff::PrimeField for Fp { 564 | type Repr = [u8; 32]; 565 | 566 | const MODULUS: &'static str = 567 | "0x40000000000000000000000000000000224698fc094cf91b992d30ed00000001"; 568 | const TWO_INV: Self = Fp::from_raw([ 569 | 0xcc96987680000001, 570 | 0x11234c7e04a67c8d, 571 | 0x0000000000000000, 572 | 0x2000000000000000, 573 | ]); 574 | const NUM_BITS: u32 = 255; 575 | const CAPACITY: u32 = 254; 576 | const MULTIPLICATIVE_GENERATOR: Self = GENERATOR; 577 | const S: u32 = S; 578 | const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; 579 | const ROOT_OF_UNITY_INV: Self = Fp::from_raw([ 580 | 0xf0b87c7db2ce91f6, 581 | 0x84a0a1d8859f066f, 582 | 0xb4ed8e647196dad1, 583 | 0x2cd5282c53116b5c, 584 | ]); 585 | const DELTA: Self = DELTA; 586 | 587 | fn from_u128(v: u128) -> Self { 588 | Fp::from_raw([v as u64, (v >> 64) as u64, 0, 0]) 589 | } 590 | 591 | fn from_repr(repr: Self::Repr) -> CtOption { 592 | let mut tmp = Fp([0, 0, 0, 0]); 593 | 594 | tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); 595 | tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); 596 | tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); 597 | tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); 598 | 599 | // Try to subtract the modulus 600 | let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); 601 | let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); 602 | let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); 603 | let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); 604 | 605 | // If the element is smaller than MODULUS then the 606 | // subtraction will underflow, producing a borrow value 607 | // of 0xffff...ffff. Otherwise, it'll be zero. 608 | let is_some = (borrow as u8) & 1; 609 | 610 | // Convert to Montgomery form by computing 611 | // (a.R^0 * R^2) / R = a.R 612 | tmp *= &R2; 613 | 614 | CtOption::new(tmp, Choice::from(is_some)) 615 | } 616 | 617 | fn to_repr(&self) -> Self::Repr { 618 | // Turn into canonical form by computing 619 | // (a.R) / R = a 620 | let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); 621 | 622 | let mut res = [0; 32]; 623 | res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); 624 | res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); 625 | res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); 626 | res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); 627 | 628 | res 629 | } 630 | 631 | fn is_odd(&self) -> Choice { 632 | Choice::from(self.to_repr()[0] & 1) 633 | } 634 | } 635 | 636 | #[cfg(all(feature = "bits", not(target_pointer_width = "64")))] 637 | type ReprBits = [u32; 8]; 638 | 639 | #[cfg(all(feature = "bits", target_pointer_width = "64"))] 640 | type ReprBits = [u64; 4]; 641 | 642 | #[cfg(feature = "bits")] 643 | #[cfg_attr(docsrs, doc(cfg(feature = "bits")))] 644 | impl PrimeFieldBits for Fp { 645 | type ReprBits = ReprBits; 646 | 647 | fn to_le_bits(&self) -> FieldBits { 648 | let bytes = self.to_repr(); 649 | 650 | #[cfg(not(target_pointer_width = "64"))] 651 | let limbs = [ 652 | u32::from_le_bytes(bytes[0..4].try_into().unwrap()), 653 | u32::from_le_bytes(bytes[4..8].try_into().unwrap()), 654 | u32::from_le_bytes(bytes[8..12].try_into().unwrap()), 655 | u32::from_le_bytes(bytes[12..16].try_into().unwrap()), 656 | u32::from_le_bytes(bytes[16..20].try_into().unwrap()), 657 | u32::from_le_bytes(bytes[20..24].try_into().unwrap()), 658 | u32::from_le_bytes(bytes[24..28].try_into().unwrap()), 659 | u32::from_le_bytes(bytes[28..32].try_into().unwrap()), 660 | ]; 661 | 662 | #[cfg(target_pointer_width = "64")] 663 | let limbs = [ 664 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 665 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 666 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 667 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 668 | ]; 669 | 670 | FieldBits::new(limbs) 671 | } 672 | 673 | fn char_le_bits() -> FieldBits { 674 | #[cfg(not(target_pointer_width = "64"))] 675 | { 676 | FieldBits::new(MODULUS_LIMBS_32) 677 | } 678 | 679 | #[cfg(target_pointer_width = "64")] 680 | FieldBits::new(MODULUS.0) 681 | } 682 | } 683 | 684 | #[cfg(feature = "sqrt-table")] 685 | lazy_static! { 686 | // The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta. 687 | #[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))] 688 | static ref FP_TABLES: SqrtTables = SqrtTables::new(0x11BE, 1098); 689 | } 690 | 691 | impl SqrtTableHelpers for Fp { 692 | fn pow_by_t_minus1_over2(&self) -> Self { 693 | let sqr = |x: Fp, i: u32| (0..i).fold(x, |x, _| x.square()); 694 | 695 | let r10 = self.square(); 696 | let r11 = r10 * self; 697 | let r110 = r11.square(); 698 | let r111 = r110 * self; 699 | let r1001 = r111 * r10; 700 | let r1101 = r111 * r110; 701 | let ra = sqr(*self, 129) * self; 702 | let rb = sqr(ra, 7) * r1001; 703 | let rc = sqr(rb, 7) * r1101; 704 | let rd = sqr(rc, 4) * r11; 705 | let re = sqr(rd, 6) * r111; 706 | let rf = sqr(re, 3) * r111; 707 | let rg = sqr(rf, 10) * r1001; 708 | let rh = sqr(rg, 5) * r1001; 709 | let ri = sqr(rh, 4) * r1001; 710 | let rj = sqr(ri, 3) * r111; 711 | let rk = sqr(rj, 4) * r1001; 712 | let rl = sqr(rk, 5) * r11; 713 | let rm = sqr(rl, 4) * r111; 714 | let rn = sqr(rm, 4) * r11; 715 | let ro = sqr(rn, 6) * r1001; 716 | let rp = sqr(ro, 5) * r1101; 717 | let rq = sqr(rp, 4) * r11; 718 | let rr = sqr(rq, 7) * r111; 719 | let rs = sqr(rr, 3) * r11; 720 | rs.square() // rt 721 | } 722 | 723 | fn get_lower_32(&self) -> u32 { 724 | // TODO: don't reduce, just hash the Montgomery form. (Requires rebuilding perfect hash table.) 725 | let tmp = Fp::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); 726 | 727 | tmp.0[0] as u32 728 | } 729 | } 730 | 731 | impl WithSmallOrderMulGroup<3> for Fp { 732 | const ZETA: Self = Fp::from_raw([ 733 | 0x1dad5ebdfdfe4ab9, 734 | 0x1d1f8bd237ad3149, 735 | 0x2caad5dc57aab1b0, 736 | 0x12ccca834acdba71, 737 | ]); 738 | } 739 | 740 | impl FromUniformBytes<64> for Fp { 741 | /// Converts a 512-bit little endian integer into 742 | /// a `Fp` by reducing by the modulus. 743 | fn from_uniform_bytes(bytes: &[u8; 64]) -> Fp { 744 | Fp::from_u512([ 745 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 746 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 747 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 748 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 749 | u64::from_le_bytes(bytes[32..40].try_into().unwrap()), 750 | u64::from_le_bytes(bytes[40..48].try_into().unwrap()), 751 | u64::from_le_bytes(bytes[48..56].try_into().unwrap()), 752 | u64::from_le_bytes(bytes[56..64].try_into().unwrap()), 753 | ]) 754 | } 755 | } 756 | 757 | #[cfg(feature = "gpu")] 758 | impl ec_gpu::GpuName for Fp { 759 | fn name() -> alloc::string::String { 760 | ec_gpu::name!() 761 | } 762 | } 763 | 764 | #[cfg(feature = "gpu")] 765 | impl ec_gpu::GpuField for Fp { 766 | fn one() -> alloc::vec::Vec { 767 | crate::fields::u64_to_u32(&R.0[..]) 768 | } 769 | 770 | fn r2() -> alloc::vec::Vec { 771 | crate::fields::u64_to_u32(&R2.0[..]) 772 | } 773 | 774 | fn modulus() -> alloc::vec::Vec { 775 | crate::fields::u64_to_u32(&MODULUS.0[..]) 776 | } 777 | } 778 | 779 | #[test] 780 | fn test_inv() { 781 | // Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating 782 | // by totient(2**64) - 1 783 | 784 | let mut inv = 1u64; 785 | for _ in 0..63 { 786 | inv = inv.wrapping_mul(inv); 787 | inv = inv.wrapping_mul(MODULUS.0[0]); 788 | } 789 | inv = inv.wrapping_neg(); 790 | 791 | assert_eq!(inv, INV); 792 | } 793 | 794 | #[test] 795 | fn test_sqrt() { 796 | // NB: TWO_INV is standing in as a "random" field element 797 | let v = (Fp::TWO_INV).square().sqrt().unwrap(); 798 | assert!(v == Fp::TWO_INV || (-v) == Fp::TWO_INV); 799 | } 800 | 801 | #[test] 802 | fn test_sqrt_32bit_overflow() { 803 | assert!((Fp::from(5)).sqrt().is_none().unwrap_u8() == 1); 804 | } 805 | 806 | #[test] 807 | fn test_pow_by_t_minus1_over2() { 808 | // NB: TWO_INV is standing in as a "random" field element 809 | let v = (Fp::TWO_INV).pow_by_t_minus1_over2(); 810 | assert!(v == ff::Field::pow_vartime(&Fp::TWO_INV, &T_MINUS1_OVER2)); 811 | } 812 | 813 | #[test] 814 | fn test_sqrt_ratio_and_alt() { 815 | // (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field 816 | let num = (Fp::TWO_INV).square(); 817 | let div = Fp::from(25); 818 | let div_inverse = div.invert().unwrap(); 819 | let expected = Fp::TWO_INV * Fp::from(5).invert().unwrap(); 820 | let (is_square, v) = Fp::sqrt_ratio(&num, &div); 821 | assert!(bool::from(is_square)); 822 | assert!(v == expected || (-v) == expected); 823 | 824 | let (is_square_alt, v_alt) = Fp::sqrt_alt(&(num * div_inverse)); 825 | assert!(bool::from(is_square_alt)); 826 | assert!(v_alt == v); 827 | 828 | // (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field 829 | let num = num * Fp::ROOT_OF_UNITY; 830 | let expected = Fp::TWO_INV * Fp::ROOT_OF_UNITY * Fp::from(5).invert().unwrap(); 831 | let (is_square, v) = Fp::sqrt_ratio(&num, &div); 832 | assert!(!bool::from(is_square)); 833 | assert!(v == expected || (-v) == expected); 834 | 835 | let (is_square_alt, v_alt) = Fp::sqrt_alt(&(num * div_inverse)); 836 | assert!(!bool::from(is_square_alt)); 837 | assert!(v_alt == v); 838 | 839 | // (true, 0), if num is zero 840 | let num = Fp::zero(); 841 | let expected = Fp::zero(); 842 | let (is_square, v) = Fp::sqrt_ratio(&num, &div); 843 | assert!(bool::from(is_square)); 844 | assert!(v == expected); 845 | 846 | let (is_square_alt, v_alt) = Fp::sqrt_alt(&(num * div_inverse)); 847 | assert!(bool::from(is_square_alt)); 848 | assert!(v_alt == v); 849 | 850 | // (false, 0), if num is nonzero and div is zero 851 | let num = (Fp::TWO_INV).square(); 852 | let div = Fp::zero(); 853 | let expected = Fp::zero(); 854 | let (is_square, v) = Fp::sqrt_ratio(&num, &div); 855 | assert!(!bool::from(is_square)); 856 | assert!(v == expected); 857 | } 858 | 859 | #[test] 860 | fn test_zeta() { 861 | assert_eq!( 862 | format!("{:?}", Fp::ZETA), 863 | "0x12ccca834acdba712caad5dc57aab1b01d1f8bd237ad31491dad5ebdfdfe4ab9" 864 | ); 865 | 866 | let a = Fp::ZETA; 867 | assert!(a != Fp::one()); 868 | let b = a * a; 869 | assert!(b != Fp::one()); 870 | let c = b * a; 871 | assert!(c == Fp::one()); 872 | } 873 | 874 | #[test] 875 | fn test_root_of_unity() { 876 | assert_eq!( 877 | Fp::ROOT_OF_UNITY.pow_vartime(&[1 << Fp::S, 0, 0, 0]), 878 | Fp::one() 879 | ); 880 | } 881 | 882 | #[test] 883 | fn test_inv_root_of_unity() { 884 | assert_eq!(Fp::ROOT_OF_UNITY_INV, Fp::ROOT_OF_UNITY.invert().unwrap()); 885 | } 886 | 887 | #[test] 888 | fn test_inv_2() { 889 | assert_eq!(Fp::TWO_INV, Fp::from(2).invert().unwrap()); 890 | } 891 | 892 | #[test] 893 | fn test_delta() { 894 | assert_eq!(Fp::DELTA, GENERATOR.pow(&[1u64 << Fp::S, 0, 0, 0])); 895 | assert_eq!( 896 | Fp::DELTA, 897 | Fp::MULTIPLICATIVE_GENERATOR.pow(&[1u64 << Fp::S, 0, 0, 0]) 898 | ); 899 | } 900 | 901 | #[cfg(not(target_pointer_width = "64"))] 902 | #[test] 903 | fn consistent_modulus_limbs() { 904 | for (a, &b) in MODULUS 905 | .0 906 | .iter() 907 | .flat_map(|&limb| { 908 | Some(limb as u32) 909 | .into_iter() 910 | .chain(Some((limb >> 32) as u32)) 911 | }) 912 | .zip(MODULUS_LIMBS_32.iter()) 913 | { 914 | assert_eq!(a, b); 915 | } 916 | } 917 | 918 | #[test] 919 | fn test_from_u512() { 920 | assert_eq!( 921 | Fp::from_raw([ 922 | 0x3daec14d565241d9, 923 | 0x0b7af45b6073944b, 924 | 0xea5b8bd611a5bd4c, 925 | 0x150160330625db3d 926 | ]), 927 | Fp::from_u512([ 928 | 0xee155641297678a1, 929 | 0xd83e156bdbfdbe65, 930 | 0xd9ccd834c68ba0b5, 931 | 0xf508ede312272758, 932 | 0x038df7cbf8228e89, 933 | 0x3505a1e4a3c74b41, 934 | 0xbfa46f775eb82db3, 935 | 0x26ebe27e262f471d 936 | ]) 937 | ); 938 | } 939 | -------------------------------------------------------------------------------- /src/fields/fq.rs: -------------------------------------------------------------------------------- 1 | use core::fmt; 2 | use core::ops::{Add, Mul, Neg, Sub}; 3 | 4 | use ff::{Field, FromUniformBytes, PrimeField, WithSmallOrderMulGroup}; 5 | use rand::RngCore; 6 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 7 | 8 | #[cfg(feature = "sqrt-table")] 9 | use lazy_static::lazy_static; 10 | 11 | #[cfg(feature = "bits")] 12 | use ff::{FieldBits, PrimeFieldBits}; 13 | 14 | use crate::arithmetic::{adc, mac, sbb, SqrtTableHelpers}; 15 | 16 | #[cfg(feature = "sqrt-table")] 17 | use crate::arithmetic::SqrtTables; 18 | 19 | /// This represents an element of $\mathbb{F}_q$ where 20 | /// 21 | /// `q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001` 22 | /// 23 | /// is the base field of the Vesta curve. 24 | // The internal representation of this type is four 64-bit unsigned 25 | // integers in little-endian order. `Fq` values are always in 26 | // Montgomery form; i.e., Fq(a) = aR mod q, with R = 2^256. 27 | #[derive(Clone, Copy, Eq)] 28 | #[repr(transparent)] 29 | pub struct Fq(pub(crate) [u64; 4]); 30 | 31 | impl fmt::Debug for Fq { 32 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 33 | let tmp = self.to_repr(); 34 | write!(f, "0x")?; 35 | for &b in tmp.iter().rev() { 36 | write!(f, "{:02x}", b)?; 37 | } 38 | Ok(()) 39 | } 40 | } 41 | 42 | impl From for Fq { 43 | fn from(bit: bool) -> Fq { 44 | if bit { 45 | Fq::one() 46 | } else { 47 | Fq::zero() 48 | } 49 | } 50 | } 51 | 52 | impl From for Fq { 53 | fn from(val: u64) -> Fq { 54 | Fq([val, 0, 0, 0]) * R2 55 | } 56 | } 57 | 58 | impl ConstantTimeEq for Fq { 59 | fn ct_eq(&self, other: &Self) -> Choice { 60 | self.0[0].ct_eq(&other.0[0]) 61 | & self.0[1].ct_eq(&other.0[1]) 62 | & self.0[2].ct_eq(&other.0[2]) 63 | & self.0[3].ct_eq(&other.0[3]) 64 | } 65 | } 66 | 67 | impl PartialEq for Fq { 68 | #[inline] 69 | fn eq(&self, other: &Self) -> bool { 70 | self.ct_eq(other).unwrap_u8() == 1 71 | } 72 | } 73 | 74 | impl core::cmp::Ord for Fq { 75 | fn cmp(&self, other: &Self) -> core::cmp::Ordering { 76 | let left = self.to_repr(); 77 | let right = other.to_repr(); 78 | left.iter() 79 | .zip(right.iter()) 80 | .rev() 81 | .find_map(|(left_byte, right_byte)| match left_byte.cmp(right_byte) { 82 | core::cmp::Ordering::Equal => None, 83 | res => Some(res), 84 | }) 85 | .unwrap_or(core::cmp::Ordering::Equal) 86 | } 87 | } 88 | 89 | impl core::cmp::PartialOrd for Fq { 90 | fn partial_cmp(&self, other: &Self) -> Option { 91 | Some(self.cmp(other)) 92 | } 93 | } 94 | 95 | impl ConditionallySelectable for Fq { 96 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 97 | Fq([ 98 | u64::conditional_select(&a.0[0], &b.0[0], choice), 99 | u64::conditional_select(&a.0[1], &b.0[1], choice), 100 | u64::conditional_select(&a.0[2], &b.0[2], choice), 101 | u64::conditional_select(&a.0[3], &b.0[3], choice), 102 | ]) 103 | } 104 | } 105 | 106 | /// Constant representing the modulus 107 | /// q = 0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001 108 | const MODULUS: Fq = Fq([ 109 | 0x8c46eb2100000001, 110 | 0x224698fc0994a8dd, 111 | 0x0, 112 | 0x4000000000000000, 113 | ]); 114 | 115 | /// The modulus as u32 limbs. 116 | #[cfg(not(target_pointer_width = "64"))] 117 | const MODULUS_LIMBS_32: [u32; 8] = [ 118 | 0x0000_0001, 119 | 0x8c46_eb21, 120 | 0x0994_a8dd, 121 | 0x2246_98fc, 122 | 0x0000_0000, 123 | 0x0000_0000, 124 | 0x0000_0000, 125 | 0x4000_0000, 126 | ]; 127 | 128 | impl<'a> Neg for &'a Fq { 129 | type Output = Fq; 130 | 131 | #[inline] 132 | fn neg(self) -> Fq { 133 | self.neg() 134 | } 135 | } 136 | 137 | impl Neg for Fq { 138 | type Output = Fq; 139 | 140 | #[inline] 141 | fn neg(self) -> Fq { 142 | -&self 143 | } 144 | } 145 | 146 | impl<'a, 'b> Sub<&'b Fq> for &'a Fq { 147 | type Output = Fq; 148 | 149 | #[inline] 150 | fn sub(self, rhs: &'b Fq) -> Fq { 151 | self.sub(rhs) 152 | } 153 | } 154 | 155 | impl<'a, 'b> Add<&'b Fq> for &'a Fq { 156 | type Output = Fq; 157 | 158 | #[inline] 159 | fn add(self, rhs: &'b Fq) -> Fq { 160 | self.add(rhs) 161 | } 162 | } 163 | 164 | impl<'a, 'b> Mul<&'b Fq> for &'a Fq { 165 | type Output = Fq; 166 | 167 | #[inline] 168 | fn mul(self, rhs: &'b Fq) -> Fq { 169 | self.mul(rhs) 170 | } 171 | } 172 | 173 | impl_binops_additive!(Fq, Fq); 174 | impl_binops_multiplicative!(Fq, Fq); 175 | 176 | impl> ::core::iter::Sum for Fq { 177 | fn sum>(iter: I) -> Self { 178 | iter.fold(Self::ZERO, |acc, item| acc + item.borrow()) 179 | } 180 | } 181 | 182 | impl> ::core::iter::Product for Fq { 183 | fn product>(iter: I) -> Self { 184 | iter.fold(Self::ONE, |acc, item| acc * item.borrow()) 185 | } 186 | } 187 | 188 | /// INV = -(q^{-1} mod 2^64) mod 2^64 189 | const INV: u64 = 0x8c46eb20ffffffff; 190 | 191 | /// R = 2^256 mod q 192 | const R: Fq = Fq([ 193 | 0x5b2b3e9cfffffffd, 194 | 0x992c350be3420567, 195 | 0xffffffffffffffff, 196 | 0x3fffffffffffffff, 197 | ]); 198 | 199 | /// R^2 = 2^512 mod q 200 | const R2: Fq = Fq([ 201 | 0xfc9678ff0000000f, 202 | 0x67bb433d891a16e3, 203 | 0x7fae231004ccf590, 204 | 0x096d41af7ccfdaa9, 205 | ]); 206 | 207 | /// R^3 = 2^768 mod q 208 | const R3: Fq = Fq([ 209 | 0x008b421c249dae4c, 210 | 0xe13bda50dba41326, 211 | 0x88fececb8e15cb63, 212 | 0x07dd97a06e6792c8, 213 | ]); 214 | 215 | /// `GENERATOR = 5 mod q` is a generator of the `q - 1` order multiplicative 216 | /// subgroup, or in other words a primitive root of the field. 217 | const GENERATOR: Fq = Fq::from_raw([ 218 | 0x0000_0000_0000_0005, 219 | 0x0000_0000_0000_0000, 220 | 0x0000_0000_0000_0000, 221 | 0x0000_0000_0000_0000, 222 | ]); 223 | 224 | const S: u32 = 32; 225 | 226 | /// GENERATOR^t where t * 2^s + 1 = q 227 | /// with t odd. In other words, this 228 | /// is a 2^s root of unity. 229 | const ROOT_OF_UNITY: Fq = Fq::from_raw([ 230 | 0xa70e2c1102b6d05f, 231 | 0x9bb97ea3c106f049, 232 | 0x9e5c4dfd492ae26e, 233 | 0x2de6a9b8746d3f58, 234 | ]); 235 | 236 | /// GENERATOR^{2^s} where t * 2^s + 1 = q 237 | /// with t odd. In other words, this 238 | /// is a t root of unity. 239 | const DELTA: Fq = Fq::from_raw([ 240 | 0x8494392472d1683c, 241 | 0xe3ac3376541d1140, 242 | 0x06f0a88e7f7949f8, 243 | 0x2237d54423724166, 244 | ]); 245 | 246 | /// `(t - 1) // 2` where t * 2^s + 1 = p with t odd. 247 | #[cfg(any(test, not(feature = "sqrt-table")))] 248 | const T_MINUS1_OVER2: [u64; 4] = [ 249 | 0x04ca_546e_c623_7590, 250 | 0x0000_0000_1123_4c7e, 251 | 0x0000_0000_0000_0000, 252 | 0x0000_0000_2000_0000, 253 | ]; 254 | 255 | impl Default for Fq { 256 | #[inline] 257 | fn default() -> Self { 258 | Self::zero() 259 | } 260 | } 261 | 262 | impl Fq { 263 | /// Returns zero, the additive identity. 264 | #[inline] 265 | pub const fn zero() -> Fq { 266 | Fq([0, 0, 0, 0]) 267 | } 268 | 269 | /// Returns one, the multiplicative identity. 270 | #[inline] 271 | pub const fn one() -> Fq { 272 | R 273 | } 274 | 275 | /// Doubles this field element. 276 | #[inline] 277 | pub const fn double(&self) -> Fq { 278 | // TODO: This can be achieved more efficiently with a bitshift. 279 | self.add(self) 280 | } 281 | 282 | fn from_u512(limbs: [u64; 8]) -> Fq { 283 | // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits 284 | // with the higher bits multiplied by 2^256. Thus, we perform two reductions 285 | // 286 | // 1. the lower bits are multiplied by R^2, as normal 287 | // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 288 | // 289 | // and computing their sum in the field. It remains to see that arbitrary 256-bit 290 | // numbers can be placed into Montgomery form safely using the reduction. The 291 | // reduction works so long as the product is less than R=2^256 multiplied by 292 | // the modulus. This holds because for any `c` smaller than the modulus, we have 293 | // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the 294 | // reduction always works so long as `c` is in the field; in this case it is either the 295 | // constant `R2` or `R3`. 296 | let d0 = Fq([limbs[0], limbs[1], limbs[2], limbs[3]]); 297 | let d1 = Fq([limbs[4], limbs[5], limbs[6], limbs[7]]); 298 | // Convert to Montgomery form 299 | d0 * R2 + d1 * R3 300 | } 301 | 302 | /// Converts from an integer represented in little endian 303 | /// into its (congruent) `Fq` representation. 304 | pub const fn from_raw(val: [u64; 4]) -> Self { 305 | (&Fq(val)).mul(&R2) 306 | } 307 | 308 | /// Squares this element. 309 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 310 | pub const fn square(&self) -> Fq { 311 | let (r1, carry) = mac(0, self.0[0], self.0[1], 0); 312 | let (r2, carry) = mac(0, self.0[0], self.0[2], carry); 313 | let (r3, r4) = mac(0, self.0[0], self.0[3], carry); 314 | 315 | let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); 316 | let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); 317 | 318 | let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); 319 | 320 | let r7 = r6 >> 63; 321 | let r6 = (r6 << 1) | (r5 >> 63); 322 | let r5 = (r5 << 1) | (r4 >> 63); 323 | let r4 = (r4 << 1) | (r3 >> 63); 324 | let r3 = (r3 << 1) | (r2 >> 63); 325 | let r2 = (r2 << 1) | (r1 >> 63); 326 | let r1 = r1 << 1; 327 | 328 | let (r0, carry) = mac(0, self.0[0], self.0[0], 0); 329 | let (r1, carry) = adc(0, r1, carry); 330 | let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); 331 | let (r3, carry) = adc(0, r3, carry); 332 | let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); 333 | let (r5, carry) = adc(0, r5, carry); 334 | let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); 335 | let (r7, _) = adc(0, r7, carry); 336 | 337 | Fq::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) 338 | } 339 | 340 | #[allow(clippy::too_many_arguments)] 341 | #[cfg_attr(not(feature = "uninline-portable"), inline(always))] 342 | const fn montgomery_reduce( 343 | r0: u64, 344 | r1: u64, 345 | r2: u64, 346 | r3: u64, 347 | r4: u64, 348 | r5: u64, 349 | r6: u64, 350 | r7: u64, 351 | ) -> Self { 352 | // The Montgomery reduction here is based on Algorithm 14.32 in 353 | // Handbook of Applied Cryptography 354 | // . 355 | 356 | let k = r0.wrapping_mul(INV); 357 | let (_, carry) = mac(r0, k, MODULUS.0[0], 0); 358 | let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); 359 | let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); 360 | let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); 361 | let (r4, carry2) = adc(r4, 0, carry); 362 | 363 | let k = r1.wrapping_mul(INV); 364 | let (_, carry) = mac(r1, k, MODULUS.0[0], 0); 365 | let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); 366 | let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); 367 | let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); 368 | let (r5, carry2) = adc(r5, carry2, carry); 369 | 370 | let k = r2.wrapping_mul(INV); 371 | let (_, carry) = mac(r2, k, MODULUS.0[0], 0); 372 | let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); 373 | let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); 374 | let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); 375 | let (r6, carry2) = adc(r6, carry2, carry); 376 | 377 | let k = r3.wrapping_mul(INV); 378 | let (_, carry) = mac(r3, k, MODULUS.0[0], 0); 379 | let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); 380 | let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); 381 | let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); 382 | let (r7, _) = adc(r7, carry2, carry); 383 | 384 | // Result may be within MODULUS of the correct value 385 | (&Fq([r4, r5, r6, r7])).sub(&MODULUS) 386 | } 387 | 388 | /// Multiplies `rhs` by `self`, returning the result. 389 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 390 | pub const fn mul(&self, rhs: &Self) -> Self { 391 | // Schoolbook multiplication 392 | 393 | let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); 394 | let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); 395 | let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); 396 | let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); 397 | 398 | let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); 399 | let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); 400 | let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); 401 | let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); 402 | 403 | let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); 404 | let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); 405 | let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); 406 | let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); 407 | 408 | let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); 409 | let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); 410 | let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); 411 | let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); 412 | 413 | Fq::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) 414 | } 415 | 416 | /// Subtracts `rhs` from `self`, returning the result. 417 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 418 | pub const fn sub(&self, rhs: &Self) -> Self { 419 | let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); 420 | let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); 421 | let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); 422 | let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); 423 | 424 | // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise 425 | // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. 426 | let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); 427 | let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); 428 | let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); 429 | let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); 430 | 431 | Fq([d0, d1, d2, d3]) 432 | } 433 | 434 | /// Adds `rhs` to `self`, returning the result. 435 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 436 | pub const fn add(&self, rhs: &Self) -> Self { 437 | let (d0, carry) = adc(self.0[0], rhs.0[0], 0); 438 | let (d1, carry) = adc(self.0[1], rhs.0[1], carry); 439 | let (d2, carry) = adc(self.0[2], rhs.0[2], carry); 440 | let (d3, _) = adc(self.0[3], rhs.0[3], carry); 441 | 442 | // Attempt to subtract the modulus, to ensure the value 443 | // is smaller than the modulus. 444 | (&Fq([d0, d1, d2, d3])).sub(&MODULUS) 445 | } 446 | 447 | /// Negates `self`. 448 | #[cfg_attr(not(feature = "uninline-portable"), inline)] 449 | pub const fn neg(&self) -> Self { 450 | // Subtract `self` from `MODULUS` to negate. Ignore the final 451 | // borrow because it cannot underflow; self is guaranteed to 452 | // be in the field. 453 | let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); 454 | let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); 455 | let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); 456 | let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); 457 | 458 | // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is 459 | // zero if `self` was zero, and `u64::max_value()` if self was nonzero. 460 | let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); 461 | 462 | Fq([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) 463 | } 464 | } 465 | 466 | impl From for [u8; 32] { 467 | fn from(value: Fq) -> [u8; 32] { 468 | value.to_repr() 469 | } 470 | } 471 | 472 | impl<'a> From<&'a Fq> for [u8; 32] { 473 | fn from(value: &'a Fq) -> [u8; 32] { 474 | value.to_repr() 475 | } 476 | } 477 | 478 | impl ff::Field for Fq { 479 | const ZERO: Self = Self::zero(); 480 | const ONE: Self = Self::one(); 481 | 482 | fn random(mut rng: impl RngCore) -> Self { 483 | Self::from_u512([ 484 | rng.next_u64(), 485 | rng.next_u64(), 486 | rng.next_u64(), 487 | rng.next_u64(), 488 | rng.next_u64(), 489 | rng.next_u64(), 490 | rng.next_u64(), 491 | rng.next_u64(), 492 | ]) 493 | } 494 | 495 | fn double(&self) -> Self { 496 | self.double() 497 | } 498 | 499 | #[inline(always)] 500 | fn square(&self) -> Self { 501 | self.square() 502 | } 503 | 504 | fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { 505 | #[cfg(feature = "sqrt-table")] 506 | { 507 | FQ_TABLES.sqrt_ratio(num, div) 508 | } 509 | 510 | #[cfg(not(feature = "sqrt-table"))] 511 | ff::helpers::sqrt_ratio_generic(num, div) 512 | } 513 | 514 | #[cfg(feature = "sqrt-table")] 515 | fn sqrt_alt(&self) -> (Choice, Self) { 516 | FQ_TABLES.sqrt_alt(self) 517 | } 518 | 519 | /// Computes the square root of this element, if it exists. 520 | fn sqrt(&self) -> CtOption { 521 | #[cfg(feature = "sqrt-table")] 522 | { 523 | let (is_square, res) = FQ_TABLES.sqrt_alt(self); 524 | CtOption::new(res, is_square) 525 | } 526 | 527 | #[cfg(not(feature = "sqrt-table"))] 528 | ff::helpers::sqrt_tonelli_shanks(self, &T_MINUS1_OVER2) 529 | } 530 | 531 | /// Computes the multiplicative inverse of this element, 532 | /// failing if the element is zero. 533 | fn invert(&self) -> CtOption { 534 | let tmp = self.pow_vartime(&[ 535 | 0x8c46eb20ffffffff, 536 | 0x224698fc0994a8dd, 537 | 0x0, 538 | 0x4000000000000000, 539 | ]); 540 | 541 | CtOption::new(tmp, !self.ct_eq(&Self::zero())) 542 | } 543 | 544 | fn pow_vartime>(&self, exp: S) -> Self { 545 | let mut res = Self::one(); 546 | let mut found_one = false; 547 | for e in exp.as_ref().iter().rev() { 548 | for i in (0..64).rev() { 549 | if found_one { 550 | res = res.square(); 551 | } 552 | 553 | if ((*e >> i) & 1) == 1 { 554 | found_one = true; 555 | res *= self; 556 | } 557 | } 558 | } 559 | res 560 | } 561 | } 562 | 563 | impl ff::PrimeField for Fq { 564 | type Repr = [u8; 32]; 565 | 566 | const MODULUS: &'static str = 567 | "0x40000000000000000000000000000000224698fc0994a8dd8c46eb2100000001"; 568 | const NUM_BITS: u32 = 255; 569 | const CAPACITY: u32 = 254; 570 | const TWO_INV: Self = Fq::from_raw([ 571 | 0xc623759080000001, 572 | 0x11234c7e04ca546e, 573 | 0x0000000000000000, 574 | 0x2000000000000000, 575 | ]); 576 | const MULTIPLICATIVE_GENERATOR: Self = GENERATOR; 577 | const S: u32 = S; 578 | const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; 579 | const ROOT_OF_UNITY_INV: Self = Fq::from_raw([ 580 | 0x57eecda0a84b6836, 581 | 0x4ad38b9084b8a80c, 582 | 0xf4c8f353124086c1, 583 | 0x2235e1a7415bf936, 584 | ]); 585 | const DELTA: Self = DELTA; 586 | 587 | fn from_u128(v: u128) -> Self { 588 | Fq::from_raw([v as u64, (v >> 64) as u64, 0, 0]) 589 | } 590 | 591 | fn from_repr(repr: Self::Repr) -> CtOption { 592 | let mut tmp = Fq([0, 0, 0, 0]); 593 | 594 | tmp.0[0] = u64::from_le_bytes(repr[0..8].try_into().unwrap()); 595 | tmp.0[1] = u64::from_le_bytes(repr[8..16].try_into().unwrap()); 596 | tmp.0[2] = u64::from_le_bytes(repr[16..24].try_into().unwrap()); 597 | tmp.0[3] = u64::from_le_bytes(repr[24..32].try_into().unwrap()); 598 | 599 | // Try to subtract the modulus 600 | let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); 601 | let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); 602 | let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); 603 | let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); 604 | 605 | // If the element is smaller than MODULUS then the 606 | // subtraction will underflow, producing a borrow value 607 | // of 0xffff...ffff. Otherwise, it'll be zero. 608 | let is_some = (borrow as u8) & 1; 609 | 610 | // Convert to Montgomery form by computing 611 | // (a.R^0 * R^2) / R = a.R 612 | tmp *= &R2; 613 | 614 | CtOption::new(tmp, Choice::from(is_some)) 615 | } 616 | 617 | fn to_repr(&self) -> Self::Repr { 618 | // Turn into canonical form by computing 619 | // (a.R) / R = a 620 | let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); 621 | 622 | let mut res = [0; 32]; 623 | res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); 624 | res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); 625 | res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); 626 | res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); 627 | 628 | res 629 | } 630 | 631 | fn is_odd(&self) -> Choice { 632 | Choice::from(self.to_repr()[0] & 1) 633 | } 634 | } 635 | 636 | #[cfg(all(feature = "bits", not(target_pointer_width = "64")))] 637 | type ReprBits = [u32; 8]; 638 | 639 | #[cfg(all(feature = "bits", target_pointer_width = "64"))] 640 | type ReprBits = [u64; 4]; 641 | 642 | #[cfg(feature = "bits")] 643 | impl PrimeFieldBits for Fq { 644 | type ReprBits = ReprBits; 645 | 646 | fn to_le_bits(&self) -> FieldBits { 647 | let bytes = self.to_repr(); 648 | 649 | #[cfg(not(target_pointer_width = "64"))] 650 | let limbs = [ 651 | u32::from_le_bytes(bytes[0..4].try_into().unwrap()), 652 | u32::from_le_bytes(bytes[4..8].try_into().unwrap()), 653 | u32::from_le_bytes(bytes[8..12].try_into().unwrap()), 654 | u32::from_le_bytes(bytes[12..16].try_into().unwrap()), 655 | u32::from_le_bytes(bytes[16..20].try_into().unwrap()), 656 | u32::from_le_bytes(bytes[20..24].try_into().unwrap()), 657 | u32::from_le_bytes(bytes[24..28].try_into().unwrap()), 658 | u32::from_le_bytes(bytes[28..32].try_into().unwrap()), 659 | ]; 660 | 661 | #[cfg(target_pointer_width = "64")] 662 | let limbs = [ 663 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 664 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 665 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 666 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 667 | ]; 668 | 669 | FieldBits::new(limbs) 670 | } 671 | 672 | fn char_le_bits() -> FieldBits { 673 | #[cfg(not(target_pointer_width = "64"))] 674 | { 675 | FieldBits::new(MODULUS_LIMBS_32) 676 | } 677 | 678 | #[cfg(target_pointer_width = "64")] 679 | FieldBits::new(MODULUS.0) 680 | } 681 | } 682 | 683 | #[cfg(feature = "sqrt-table")] 684 | lazy_static! { 685 | // The perfect hash parameters are found by `squareroottab.sage` in zcash/pasta. 686 | #[cfg_attr(docsrs, doc(cfg(feature = "sqrt-table")))] 687 | static ref FQ_TABLES: SqrtTables = SqrtTables::new(0x116A9E, 1206); 688 | } 689 | 690 | impl SqrtTableHelpers for Fq { 691 | fn pow_by_t_minus1_over2(&self) -> Self { 692 | let sqr = |x: Fq, i: u32| (0..i).fold(x, |x, _| x.square()); 693 | 694 | let s10 = self.square(); 695 | let s11 = s10 * self; 696 | let s111 = s11.square() * self; 697 | let s1001 = s111 * s10; 698 | let s1011 = s1001 * s10; 699 | let s1101 = s1011 * s10; 700 | let sa = sqr(*self, 129) * self; 701 | let sb = sqr(sa, 7) * s1001; 702 | let sc = sqr(sb, 7) * s1101; 703 | let sd = sqr(sc, 4) * s11; 704 | let se = sqr(sd, 6) * s111; 705 | let sf = sqr(se, 3) * s111; 706 | let sg = sqr(sf, 10) * s1001; 707 | let sh = sqr(sg, 4) * s1001; 708 | let si = sqr(sh, 5) * s1001; 709 | let sj = sqr(si, 5) * s1001; 710 | let sk = sqr(sj, 3) * s1001; 711 | let sl = sqr(sk, 4) * s1011; 712 | let sm = sqr(sl, 4) * s1011; 713 | let sn = sqr(sm, 5) * s11; 714 | let so = sqr(sn, 4) * self; 715 | let sp = sqr(so, 5) * s11; 716 | let sq = sqr(sp, 4) * s111; 717 | let sr = sqr(sq, 5) * s1011; 718 | let ss = sqr(sr, 3) * self; 719 | sqr(ss, 4) // st 720 | } 721 | 722 | fn get_lower_32(&self) -> u32 { 723 | // TODO: don't reduce, just hash the Montgomery form. (Requires rebuilding perfect hash table.) 724 | let tmp = Fq::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); 725 | 726 | tmp.0[0] as u32 727 | } 728 | } 729 | 730 | impl WithSmallOrderMulGroup<3> for Fq { 731 | const ZETA: Self = Fq::from_raw([ 732 | 0x2aa9d2e050aa0e4f, 733 | 0x0fed467d47c033af, 734 | 0x511db4d81cf70f5a, 735 | 0x06819a58283e528e, 736 | ]); 737 | } 738 | 739 | impl FromUniformBytes<64> for Fq { 740 | /// Converts a 512-bit little endian integer into 741 | /// a `Fq` by reducing by the modulus. 742 | fn from_uniform_bytes(bytes: &[u8; 64]) -> Fq { 743 | Fq::from_u512([ 744 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 745 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 746 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 747 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 748 | u64::from_le_bytes(bytes[32..40].try_into().unwrap()), 749 | u64::from_le_bytes(bytes[40..48].try_into().unwrap()), 750 | u64::from_le_bytes(bytes[48..56].try_into().unwrap()), 751 | u64::from_le_bytes(bytes[56..64].try_into().unwrap()), 752 | ]) 753 | } 754 | } 755 | 756 | #[cfg(feature = "gpu")] 757 | impl ec_gpu::GpuName for Fq { 758 | fn name() -> alloc::string::String { 759 | ec_gpu::name!() 760 | } 761 | } 762 | 763 | #[cfg(feature = "gpu")] 764 | impl ec_gpu::GpuField for Fq { 765 | fn one() -> alloc::vec::Vec { 766 | crate::fields::u64_to_u32(&R.0[..]) 767 | } 768 | 769 | fn r2() -> alloc::vec::Vec { 770 | crate::fields::u64_to_u32(&R2.0[..]) 771 | } 772 | 773 | fn modulus() -> alloc::vec::Vec { 774 | crate::fields::u64_to_u32(&MODULUS.0[..]) 775 | } 776 | } 777 | 778 | #[test] 779 | fn test_inv() { 780 | // Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating 781 | // by totient(2**64) - 1 782 | 783 | let mut inv = 1u64; 784 | for _ in 0..63 { 785 | inv = inv.wrapping_mul(inv); 786 | inv = inv.wrapping_mul(MODULUS.0[0]); 787 | } 788 | inv = inv.wrapping_neg(); 789 | 790 | assert_eq!(inv, INV); 791 | } 792 | 793 | #[test] 794 | fn test_sqrt() { 795 | // NB: TWO_INV is standing in as a "random" field element 796 | let v = (Fq::TWO_INV).square().sqrt().unwrap(); 797 | assert!(v == Fq::TWO_INV || (-v) == Fq::TWO_INV); 798 | } 799 | 800 | #[test] 801 | fn test_sqrt_32bit_overflow() { 802 | assert!((Fq::from(5)).sqrt().is_none().unwrap_u8() == 1); 803 | } 804 | 805 | #[test] 806 | fn test_pow_by_t_minus1_over2() { 807 | // NB: TWO_INV is standing in as a "random" field element 808 | let v = (Fq::TWO_INV).pow_by_t_minus1_over2(); 809 | assert!(v == ff::Field::pow_vartime(&Fq::TWO_INV, &T_MINUS1_OVER2)); 810 | } 811 | 812 | #[test] 813 | fn test_sqrt_ratio_and_alt() { 814 | // (true, sqrt(num/div)), if num and div are nonzero and num/div is a square in the field 815 | let num = (Fq::TWO_INV).square(); 816 | let div = Fq::from(25); 817 | let div_inverse = div.invert().unwrap(); 818 | let expected = Fq::TWO_INV * Fq::from(5).invert().unwrap(); 819 | let (is_square, v) = Fq::sqrt_ratio(&num, &div); 820 | assert!(bool::from(is_square)); 821 | assert!(v == expected || (-v) == expected); 822 | 823 | let (is_square_alt, v_alt) = Fq::sqrt_alt(&(num * div_inverse)); 824 | assert!(bool::from(is_square_alt)); 825 | assert!(v_alt == v); 826 | 827 | // (false, sqrt(ROOT_OF_UNITY * num/div)), if num and div are nonzero and num/div is a nonsquare in the field 828 | let num = num * Fq::ROOT_OF_UNITY; 829 | let expected = Fq::TWO_INV * Fq::ROOT_OF_UNITY * Fq::from(5).invert().unwrap(); 830 | let (is_square, v) = Fq::sqrt_ratio(&num, &div); 831 | assert!(!bool::from(is_square)); 832 | assert!(v == expected || (-v) == expected); 833 | 834 | let (is_square_alt, v_alt) = Fq::sqrt_alt(&(num * div_inverse)); 835 | assert!(!bool::from(is_square_alt)); 836 | assert!(v_alt == v); 837 | 838 | // (true, 0), if num is zero 839 | let num = Fq::zero(); 840 | let expected = Fq::zero(); 841 | let (is_square, v) = Fq::sqrt_ratio(&num, &div); 842 | assert!(bool::from(is_square)); 843 | assert!(v == expected); 844 | 845 | let (is_square_alt, v_alt) = Fq::sqrt_alt(&(num * div_inverse)); 846 | assert!(bool::from(is_square_alt)); 847 | assert!(v_alt == v); 848 | 849 | // (false, 0), if num is nonzero and div is zero 850 | let num = (Fq::TWO_INV).square(); 851 | let div = Fq::zero(); 852 | let expected = Fq::zero(); 853 | let (is_square, v) = Fq::sqrt_ratio(&num, &div); 854 | assert!(!bool::from(is_square)); 855 | assert!(v == expected); 856 | } 857 | 858 | #[test] 859 | fn test_zeta() { 860 | assert_eq!( 861 | format!("{:?}", Fq::ZETA), 862 | "0x06819a58283e528e511db4d81cf70f5a0fed467d47c033af2aa9d2e050aa0e4f" 863 | ); 864 | let a = Fq::ZETA; 865 | assert!(a != Fq::one()); 866 | let b = a * a; 867 | assert!(b != Fq::one()); 868 | let c = b * a; 869 | assert!(c == Fq::one()); 870 | } 871 | 872 | #[test] 873 | fn test_root_of_unity() { 874 | assert_eq!( 875 | Fq::ROOT_OF_UNITY.pow_vartime(&[1 << Fq::S, 0, 0, 0]), 876 | Fq::one() 877 | ); 878 | } 879 | 880 | #[test] 881 | fn test_inv_root_of_unity() { 882 | assert_eq!(Fq::ROOT_OF_UNITY_INV, Fq::ROOT_OF_UNITY.invert().unwrap()); 883 | } 884 | 885 | #[test] 886 | fn test_inv_2() { 887 | assert_eq!(Fq::TWO_INV, Fq::from(2).invert().unwrap()); 888 | } 889 | 890 | #[test] 891 | fn test_delta() { 892 | assert_eq!(Fq::DELTA, GENERATOR.pow(&[1u64 << Fq::S, 0, 0, 0])); 893 | assert_eq!( 894 | Fq::DELTA, 895 | Fq::MULTIPLICATIVE_GENERATOR.pow(&[1u64 << Fq::S, 0, 0, 0]) 896 | ); 897 | } 898 | 899 | #[cfg(not(target_pointer_width = "64"))] 900 | #[test] 901 | fn consistent_modulus_limbs() { 902 | for (a, &b) in MODULUS 903 | .0 904 | .iter() 905 | .flat_map(|&limb| { 906 | Some(limb as u32) 907 | .into_iter() 908 | .chain(Some((limb >> 32) as u32)) 909 | }) 910 | .zip(MODULUS_LIMBS_32.iter()) 911 | { 912 | assert_eq!(a, b); 913 | } 914 | } 915 | 916 | #[test] 917 | fn test_from_u512() { 918 | assert_eq!( 919 | Fq::from_raw([ 920 | 0xe22bd0d1b22cc43e, 921 | 0x6b84e5b52490a7c8, 922 | 0x264262941ac9e229, 923 | 0x27dcfdf361ce4254 924 | ]), 925 | Fq::from_u512([ 926 | 0x64a80cce0b5a2369, 927 | 0x84f2ef0501bc783c, 928 | 0x696e5e63c86bbbde, 929 | 0x924072f52dc6cc62, 930 | 0x8288a507c8d61128, 931 | 0x3b2efb1ef697e3fe, 932 | 0x75a4998d06855f27, 933 | 0x52ea589e69712cc0 934 | ]) 935 | ); 936 | } 937 | -------------------------------------------------------------------------------- /src/hashtocurve.rs: -------------------------------------------------------------------------------- 1 | //! This module implements "simplified SWU" hashing to short Weierstrass curves 2 | //! with a = 0. 3 | 4 | use ff::{Field, FromUniformBytes, PrimeField}; 5 | use static_assertions::const_assert; 6 | use subtle::ConstantTimeEq; 7 | 8 | use crate::arithmetic::CurveExt; 9 | 10 | /// Hashes over a message and writes the output to all of `buf`. 11 | pub fn hash_to_field>( 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!((22 + 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_SSWU_RO_") 43 | .update(&[(22 + curve_id.len() + domain_prefix.len()) as u8]) 44 | .finalize(); 45 | 46 | let b_1 = empty_hasher 47 | .clone() 48 | .update(b_0.as_array()) 49 | .update(&[1]) 50 | .update(domain_prefix.as_bytes()) 51 | .update(b"-") 52 | .update(curve_id.as_bytes()) 53 | .update(b"_XMD:BLAKE2b_SSWU_RO_") 54 | .update(&[(22 + curve_id.len() + domain_prefix.len()) as u8]) 55 | .finalize(); 56 | 57 | let b_2 = { 58 | let mut empty_hasher = empty_hasher; 59 | for (l, r) in b_0.as_array().iter().zip(b_1.as_array().iter()) { 60 | empty_hasher.update(&[*l ^ *r]); 61 | } 62 | empty_hasher 63 | .update(&[2]) 64 | .update(domain_prefix.as_bytes()) 65 | .update(b"-") 66 | .update(curve_id.as_bytes()) 67 | .update(b"_XMD:BLAKE2b_SSWU_RO_") 68 | .update(&[(22 + curve_id.len() + domain_prefix.len()) as u8]) 69 | .finalize() 70 | }; 71 | 72 | for (big, buf) in [b_1, b_2].iter().zip(buf.iter_mut()) { 73 | let mut little = [0u8; CHUNKLEN]; 74 | little.copy_from_slice(big.as_array()); 75 | little.reverse(); 76 | *buf = F::from_uniform_bytes(&little); 77 | } 78 | } 79 | 80 | /// Implements a degree 3 isogeny map. 81 | pub fn iso_map, I: CurveExt>( 82 | p: &I, 83 | iso: &[C::Base; 13], 84 | ) -> C { 85 | // The input and output are in Jacobian coordinates, using the method 86 | // in "Avoiding inversions" [WB2019, section 4.3]. 87 | 88 | let (x, y, z) = p.jacobian_coordinates(); 89 | 90 | let z2 = z.square(); 91 | let z3 = z2 * z; 92 | let z4 = z2.square(); 93 | let z6 = z3.square(); 94 | 95 | let num_x = ((iso[0] * x + iso[1] * z2) * x + iso[2] * z4) * x + iso[3] * z6; 96 | let div_x = (z2 * x + iso[4] * z4) * x + iso[5] * z6; 97 | 98 | let num_y = (((iso[6] * x + iso[7] * z2) * x + iso[8] * z4) * x + iso[9] * z6) * y; 99 | let div_y = (((x + iso[10] * z2) * x + iso[11] * z4) * x + iso[12] * z6) * z3; 100 | 101 | let zo = div_x * div_y; 102 | let xo = num_x * div_y * zo; 103 | let yo = num_y * div_x * zo.square(); 104 | 105 | C::new_jacobian(xo, yo, zo).unwrap() 106 | } 107 | 108 | #[allow(clippy::many_single_char_names)] 109 | pub fn map_to_curve_simple_swu, I: CurveExt>( 110 | u: &F, 111 | theta: F, 112 | z: F, 113 | ) -> I { 114 | // 1. tv1 = inv0(Z^2 * u^4 + Z * u^2) 115 | // 2. x1 = (-B / A) * (1 + tv1) 116 | // 3. If tv1 == 0, set x1 = B / (Z * A) 117 | // 4. gx1 = x1^3 + A * x1 + B 118 | // 119 | // We use the "Avoiding inversions" optimization in [WB2019, section 4.2] 120 | // (not to be confused with section 4.3): 121 | // 122 | // here [WB2019] 123 | // ------- --------------------------------- 124 | // Z ξ 125 | // u t 126 | // Z * u^2 ξ * t^2 (called u, confusingly) 127 | // x1 X_0(t) 128 | // x2 X_1(t) 129 | // gx1 g(X_0(t)) 130 | // gx2 g(X_1(t)) 131 | // 132 | // Using the "here" names: 133 | // x1 = num_x1/div = [B*(Z^2 * u^4 + Z * u^2 + 1)] / [-A*(Z^2 * u^4 + Z * u^2] 134 | // gx1 = num_gx1/div_gx1 = [num_x1^3 + A * num_x1 * div^2 + B * div^3] / div^3 135 | 136 | let a = I::a(); 137 | let b = I::b(); 138 | let z_u2 = z * u.square(); 139 | let ta = z_u2.square() + z_u2; 140 | let num_x1 = b * (ta + F::ONE); 141 | let div = a * F::conditional_select(&-ta, &z, ta.is_zero()); 142 | let num2_x1 = num_x1.square(); 143 | let div2 = div.square(); 144 | let div3 = div2 * div; 145 | let num_gx1 = (num2_x1 + a * div2) * num_x1 + b * div3; 146 | 147 | // 5. x2 = Z * u^2 * x1 148 | let num_x2 = z_u2 * num_x1; // same div 149 | 150 | // 6. gx2 = x2^3 + A * x2 + B [optimized out; see below] 151 | // 7. If is_square(gx1), set x = x1 and y = sqrt(gx1) 152 | // 8. Else set x = x2 and y = sqrt(gx2) 153 | let (gx1_square, y1) = F::sqrt_ratio(&num_gx1, &div3); 154 | 155 | // This magic also comes from a generalization of [WB2019, section 4.2]. 156 | // 157 | // The Sarkar square root algorithm with input s gives us a square root of 158 | // h * s for free when s is not square, where h is a fixed nonsquare. 159 | // In our implementation, h = ROOT_OF_UNITY. 160 | // We know that Z / h is a square since both Z and h are 161 | // nonsquares. Precompute theta as a square root of Z / ROOT_OF_UNITY. 162 | // 163 | // We have gx2 = g(Z * u^2 * x1) = Z^3 * u^6 * gx1 164 | // = (Z * u^3)^2 * (Z/h * h * gx1) 165 | // = (Z * theta * u^3)^2 * (h * gx1) 166 | // 167 | // When gx1 is not square, y1 is a square root of h * gx1, and so Z * theta * u^3 * y1 168 | // is a square root of gx2. Note that we don't actually need to compute gx2. 169 | 170 | let y2 = theta * z_u2 * u * y1; 171 | let num_x = F::conditional_select(&num_x2, &num_x1, gx1_square); 172 | let y = F::conditional_select(&y2, &y1, gx1_square); 173 | 174 | // 9. If sgn0(u) != sgn0(y), set y = -y 175 | let y = F::conditional_select(&(-y), &y, u.is_odd().ct_eq(&y.is_odd())); 176 | 177 | I::new_jacobian(num_x * div, y * div3, div).unwrap() 178 | } 179 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the Pallas / Vesta curve cycle. 2 | 3 | #![no_std] 4 | #![cfg_attr(docsrs, feature(doc_cfg))] 5 | #![allow(unknown_lints)] 6 | #![allow(clippy::op_ref, clippy::same_item_push, clippy::upper_case_acronyms)] 7 | #![deny(rustdoc::broken_intra_doc_links)] 8 | #![deny(missing_debug_implementations)] 9 | #![deny(missing_docs)] 10 | #![deny(unsafe_code)] 11 | 12 | #[cfg(feature = "alloc")] 13 | extern crate alloc; 14 | 15 | #[cfg(test)] 16 | #[macro_use] 17 | extern crate std; 18 | 19 | #[macro_use] 20 | mod macros; 21 | mod curves; 22 | mod fields; 23 | 24 | pub mod arithmetic; 25 | pub mod pallas; 26 | pub mod vesta; 27 | 28 | #[cfg(feature = "alloc")] 29 | mod hashtocurve; 30 | 31 | #[cfg(feature = "serde")] 32 | mod serde_impl; 33 | 34 | pub use curves::*; 35 | pub use fields::*; 36 | 37 | pub extern crate group; 38 | 39 | #[cfg(feature = "alloc")] 40 | #[test] 41 | fn test_endo_consistency() { 42 | use crate::arithmetic::CurveExt; 43 | use group::{ff::WithSmallOrderMulGroup, Group}; 44 | 45 | let a = pallas::Point::generator(); 46 | assert_eq!(a * pallas::Scalar::ZETA, a.endo()); 47 | let a = vesta::Point::generator(); 48 | assert_eq!(a * vesta::Scalar::ZETA, a.endo()); 49 | } 50 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | macro_rules! impl_add_binop_specify_output { 2 | ($lhs:ident, $rhs:ident, $output:ident) => { 3 | impl<'b> ::core::ops::Add<&'b $rhs> for $lhs { 4 | type Output = $output; 5 | 6 | #[inline] 7 | fn add(self, rhs: &'b $rhs) -> $output { 8 | &self + rhs 9 | } 10 | } 11 | 12 | impl<'a> ::core::ops::Add<$rhs> for &'a $lhs { 13 | type Output = $output; 14 | 15 | #[inline] 16 | fn add(self, rhs: $rhs) -> $output { 17 | self + &rhs 18 | } 19 | } 20 | 21 | impl ::core::ops::Add<$rhs> for $lhs { 22 | type Output = $output; 23 | 24 | #[inline] 25 | fn add(self, rhs: $rhs) -> $output { 26 | &self + &rhs 27 | } 28 | } 29 | }; 30 | } 31 | 32 | macro_rules! impl_sub_binop_specify_output { 33 | ($lhs:ident, $rhs:ident, $output:ident) => { 34 | impl<'b> ::core::ops::Sub<&'b $rhs> for $lhs { 35 | type Output = $output; 36 | 37 | #[inline] 38 | fn sub(self, rhs: &'b $rhs) -> $output { 39 | &self - rhs 40 | } 41 | } 42 | 43 | impl<'a> ::core::ops::Sub<$rhs> for &'a $lhs { 44 | type Output = $output; 45 | 46 | #[inline] 47 | fn sub(self, rhs: $rhs) -> $output { 48 | self - &rhs 49 | } 50 | } 51 | 52 | impl ::core::ops::Sub<$rhs> for $lhs { 53 | type Output = $output; 54 | 55 | #[inline] 56 | fn sub(self, rhs: $rhs) -> $output { 57 | &self - &rhs 58 | } 59 | } 60 | }; 61 | } 62 | 63 | macro_rules! impl_binops_additive_specify_output { 64 | ($lhs:ident, $rhs:ident, $output:ident) => { 65 | impl_add_binop_specify_output!($lhs, $rhs, $output); 66 | impl_sub_binop_specify_output!($lhs, $rhs, $output); 67 | }; 68 | } 69 | 70 | macro_rules! impl_binops_multiplicative_mixed { 71 | ($lhs:ident, $rhs:ident, $output:ident) => { 72 | impl<'b> ::core::ops::Mul<&'b $rhs> for $lhs { 73 | type Output = $output; 74 | 75 | #[inline] 76 | fn mul(self, rhs: &'b $rhs) -> $output { 77 | &self * rhs 78 | } 79 | } 80 | 81 | impl<'a> ::core::ops::Mul<$rhs> for &'a $lhs { 82 | type Output = $output; 83 | 84 | #[inline] 85 | fn mul(self, rhs: $rhs) -> $output { 86 | self * &rhs 87 | } 88 | } 89 | 90 | impl ::core::ops::Mul<$rhs> for $lhs { 91 | type Output = $output; 92 | 93 | #[inline] 94 | fn mul(self, rhs: $rhs) -> $output { 95 | &self * &rhs 96 | } 97 | } 98 | }; 99 | } 100 | 101 | macro_rules! impl_binops_additive { 102 | ($lhs:ident, $rhs:ident) => { 103 | impl_binops_additive_specify_output!($lhs, $rhs, $lhs); 104 | 105 | impl ::core::ops::SubAssign<$rhs> for $lhs { 106 | #[inline] 107 | fn sub_assign(&mut self, rhs: $rhs) { 108 | *self = &*self - &rhs; 109 | } 110 | } 111 | 112 | impl ::core::ops::AddAssign<$rhs> for $lhs { 113 | #[inline] 114 | fn add_assign(&mut self, rhs: $rhs) { 115 | *self = &*self + &rhs; 116 | } 117 | } 118 | 119 | impl<'b> ::core::ops::SubAssign<&'b $rhs> for $lhs { 120 | #[inline] 121 | fn sub_assign(&mut self, rhs: &'b $rhs) { 122 | *self = &*self - rhs; 123 | } 124 | } 125 | 126 | impl<'b> ::core::ops::AddAssign<&'b $rhs> for $lhs { 127 | #[inline] 128 | fn add_assign(&mut self, rhs: &'b $rhs) { 129 | *self = &*self + rhs; 130 | } 131 | } 132 | }; 133 | } 134 | 135 | macro_rules! impl_binops_multiplicative { 136 | ($lhs:ident, $rhs:ident) => { 137 | impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); 138 | 139 | impl ::core::ops::MulAssign<$rhs> for $lhs { 140 | #[inline] 141 | fn mul_assign(&mut self, rhs: $rhs) { 142 | *self = &*self * &rhs; 143 | } 144 | } 145 | 146 | impl<'b> ::core::ops::MulAssign<&'b $rhs> for $lhs { 147 | #[inline] 148 | fn mul_assign(&mut self, rhs: &'b $rhs) { 149 | *self = &*self * rhs; 150 | } 151 | } 152 | }; 153 | } 154 | -------------------------------------------------------------------------------- /src/pallas.rs: -------------------------------------------------------------------------------- 1 | //! The Pallas and iso-Pallas elliptic curve groups. 2 | 3 | use super::{Ep, EpAffine, Fp, Fq}; 4 | 5 | /// The base field of the Pallas and iso-Pallas curves. 6 | pub type Base = Fp; 7 | 8 | /// The scalar field of the Pallas and iso-Pallas curves. 9 | pub type Scalar = Fq; 10 | 11 | /// A Pallas point in the projective coordinate space. 12 | pub type Point = Ep; 13 | 14 | /// A Pallas point in the affine coordinate space (or the point at infinity). 15 | pub type Affine = EpAffine; 16 | 17 | #[cfg(feature = "alloc")] 18 | #[test] 19 | #[allow(clippy::many_single_char_names)] 20 | fn test_iso_map() { 21 | use crate::arithmetic::CurveExt; 22 | use group::Group; 23 | 24 | // This is a regression test (it's the same input to iso_map as for hash_to_curve 25 | // with domain prefix "z.cash:test", Shake128, and input b"hello"). We don't 26 | // implement Shake128 any more but that's fine. 27 | let r = super::IsoEp::new_jacobian( 28 | Base::from_raw([ 29 | 0xc37f111df5c4419e, 30 | 0x593c053e5e2337ad, 31 | 0x9c6cfc47bce1aba6, 32 | 0x0a881e4d556945aa, 33 | ]), 34 | Base::from_raw([ 35 | 0xf234e04434502b47, 36 | 0x6979f7f2b0acf188, 37 | 0xa62eec46f662cb4e, 38 | 0x035e5c8a06d5cfb4, 39 | ]), 40 | Base::from_raw([ 41 | 0x11ab791d4fb6f6b4, 42 | 0x575baa717958ef1f, 43 | 0x6ac4e343558dcbf3, 44 | 0x3af37975b0933125, 45 | ]), 46 | ) 47 | .unwrap(); 48 | let p = super::hashtocurve::iso_map::<_, Point, super::IsoEp>(&r, &Ep::ISOGENY_CONSTANTS); 49 | let (x, y, z) = p.jacobian_coordinates(); 50 | assert!( 51 | format!("{:?}", x) == "0x318cc15f281662b3f26d0175cab97b924870c837879cac647e877be51a85e898" 52 | ); 53 | assert!( 54 | format!("{:?}", y) == "0x1e91e2fa2a5a6a5bc86ff9564ae9336084470e7119dffcb85ae8c1383a3defd7" 55 | ); 56 | assert!( 57 | format!("{:?}", z) == "0x1e049436efa754f5f189aec69c2c3a4a559eca6a12b45c3f2e4a769deeca6187" 58 | ); 59 | 60 | // check that iso_map([2] r) = [2] iso_map(r) 61 | let r2 = r.double(); 62 | assert!(bool::from(r2.is_on_curve())); 63 | let p2 = super::hashtocurve::iso_map::<_, Point, super::IsoEp>(&r2, &Ep::ISOGENY_CONSTANTS); 64 | assert!(bool::from(p2.is_on_curve())); 65 | assert!(p2 == p.double()); 66 | } 67 | 68 | #[cfg(feature = "alloc")] 69 | #[test] 70 | fn test_iso_map_identity() { 71 | use crate::arithmetic::CurveExt; 72 | use group::Group; 73 | 74 | let r = super::IsoEp::new_jacobian( 75 | Base::from_raw([ 76 | 0xc37f111df5c4419e, 77 | 0x593c053e5e2337ad, 78 | 0x9c6cfc47bce1aba6, 79 | 0x0a881e4d556945aa, 80 | ]), 81 | Base::from_raw([ 82 | 0xf234e04434502b47, 83 | 0x6979f7f2b0acf188, 84 | 0xa62eec46f662cb4e, 85 | 0x035e5c8a06d5cfb4, 86 | ]), 87 | Base::from_raw([ 88 | 0x11ab791d4fb6f6b4, 89 | 0x575baa717958ef1f, 90 | 0x6ac4e343558dcbf3, 91 | 0x3af37975b0933125, 92 | ]), 93 | ) 94 | .unwrap(); 95 | let r = (r * -Fq::one()) + r; 96 | assert!(bool::from(r.is_on_curve())); 97 | assert!(bool::from(r.is_identity())); 98 | let p = super::hashtocurve::iso_map::<_, Point, super::IsoEp>(&r, &Ep::ISOGENY_CONSTANTS); 99 | assert!(bool::from(p.is_on_curve())); 100 | assert!(bool::from(p.is_identity())); 101 | } 102 | 103 | #[cfg(feature = "alloc")] 104 | #[test] 105 | fn test_map_to_curve_simple_swu() { 106 | use crate::arithmetic::CurveExt; 107 | use crate::curves::IsoEp; 108 | use crate::hashtocurve::map_to_curve_simple_swu; 109 | 110 | // The zero input is a special case. 111 | let p: IsoEp = map_to_curve_simple_swu::(&Fp::zero(), Ep::THETA, Ep::Z); 112 | let (x, y, z) = p.jacobian_coordinates(); 113 | 114 | assert!( 115 | format!("{:?}", x) == "0x28c1a6a534f56c52e25295b339129a8af5f42525dea727f485ca3433519b096e" 116 | ); 117 | assert!( 118 | format!("{:?}", y) == "0x3bfc658bee6653c63c7d7f0927083fd315d29c270207b7c7084fa1ee6ac5ae8d" 119 | ); 120 | assert!( 121 | format!("{:?}", z) == "0x054b3ba10416dc104157b1318534a19d5d115472da7d746f8a5f250cd8cdef36" 122 | ); 123 | 124 | let p: IsoEp = map_to_curve_simple_swu::(&Fp::one(), Ep::THETA, Ep::Z); 125 | let (x, y, z) = p.jacobian_coordinates(); 126 | 127 | assert!( 128 | format!("{:?}", x) == "0x010cba5957e876534af5e967c026a1856d64b071068280837913b9a5a3561505" 129 | ); 130 | assert!( 131 | format!("{:?}", y) == "0x062fc61f9cd3118e7d6e65a065ebf46a547514d6b08078e976fa6d515dcc9c81" 132 | ); 133 | assert!( 134 | format!("{:?}", z) == "0x3f86cb8c311250c3101c4e523e7793605ccff5623de1753a7c75bc9a29a73688" 135 | ); 136 | } 137 | 138 | #[cfg(feature = "alloc")] 139 | #[test] 140 | fn test_hash_to_curve() { 141 | use crate::arithmetic::CurveExt; 142 | use group::Group; 143 | 144 | // This test vector is chosen so that the first map_to_curve_simple_swu takes the gx1 square 145 | // "branch" and the second takes the gx1 non-square "branch" (opposite to the Vesta test vector). 146 | let hash = Point::hash_to_curve("z.cash:test"); 147 | let p: Point = hash(b"Trans rights now!"); 148 | let (x, y, z) = p.jacobian_coordinates(); 149 | 150 | assert!( 151 | format!("{:?}", x) == "0x36a6e3a9c50b7b6540cb002c977c82f37f8a875fb51eb35327ee1452e6ce7947" 152 | ); 153 | assert!( 154 | format!("{:?}", y) == "0x01da3b4403d73252f2d7e9c19bc23dc6a080f2d02f8262fca4f7e3d756ac6a7c" 155 | ); 156 | assert!( 157 | format!("{:?}", z) == "0x1d48103df8fcbb70d1809c1806c95651dd884a559fec0549658537ce9d94bed9" 158 | ); 159 | assert!(bool::from(p.is_on_curve())); 160 | 161 | let p = (p * -Fq::one()) + p; 162 | assert!(bool::from(p.is_on_curve())); 163 | assert!(bool::from(p.is_identity())); 164 | } 165 | -------------------------------------------------------------------------------- /src/serde_impl.rs: -------------------------------------------------------------------------------- 1 | use ff::PrimeField; 2 | use group::GroupEncoding; 3 | use serde_crate::{ 4 | de::Error as DeserializeError, Deserialize, Deserializer, Serialize, Serializer, 5 | }; 6 | 7 | use crate::{ 8 | curves::{Ep, EpAffine, Eq, EqAffine}, 9 | fields::{Fp, Fq}, 10 | group::Curve, 11 | }; 12 | 13 | /// Serializes bytes to human readable or compact representation. 14 | /// 15 | /// Depending on whether the serializer is a human readable one or not, the bytes are either 16 | /// encoded as a hex string or a list of bytes. 17 | fn serialize_bytes(bytes: [u8; 32], s: S) -> Result { 18 | if s.is_human_readable() { 19 | hex::serde::serialize(bytes, s) 20 | } else { 21 | bytes.serialize(s) 22 | } 23 | } 24 | 25 | /// Deserialize bytes from human readable or compact representation. 26 | /// 27 | /// Depending on whether the deserializer is a human readable one or not, the bytes are either 28 | /// decoded from a hex string or a list of bytes. 29 | fn deserialize_bytes<'de, D: Deserializer<'de>>(d: D) -> Result<[u8; 32], D::Error> { 30 | if d.is_human_readable() { 31 | hex::serde::deserialize(d) 32 | } else { 33 | <[u8; 32]>::deserialize(d) 34 | } 35 | } 36 | 37 | impl Serialize for Fp { 38 | fn serialize(&self, s: S) -> Result { 39 | serialize_bytes(self.to_repr(), s) 40 | } 41 | } 42 | 43 | impl<'de> Deserialize<'de> for Fp { 44 | fn deserialize>(d: D) -> Result { 45 | let bytes = deserialize_bytes(d)?; 46 | match Fp::from_repr(bytes).into() { 47 | Some(fq) => Ok(fq), 48 | None => Err(D::Error::custom( 49 | "deserialized bytes don't encode a Pallas field element", 50 | )), 51 | } 52 | } 53 | } 54 | 55 | impl Serialize for Fq { 56 | fn serialize(&self, s: S) -> Result { 57 | serialize_bytes(self.to_repr(), s) 58 | } 59 | } 60 | 61 | impl<'de> Deserialize<'de> for Fq { 62 | fn deserialize>(d: D) -> Result { 63 | let bytes = deserialize_bytes(d)?; 64 | match Fq::from_repr(bytes).into() { 65 | Some(fq) => Ok(fq), 66 | None => Err(D::Error::custom( 67 | "deserialized bytes don't encode a Vesta field element", 68 | )), 69 | } 70 | } 71 | } 72 | 73 | impl Serialize for EpAffine { 74 | fn serialize(&self, s: S) -> Result { 75 | serialize_bytes(self.to_bytes(), s) 76 | } 77 | } 78 | 79 | impl<'de> Deserialize<'de> for EpAffine { 80 | fn deserialize>(d: D) -> Result { 81 | let bytes = deserialize_bytes(d)?; 82 | match EpAffine::from_bytes(&bytes).into() { 83 | Some(ep_affine) => Ok(ep_affine), 84 | None => Err(D::Error::custom( 85 | "deserialized bytes don't encode a Pallas curve point", 86 | )), 87 | } 88 | } 89 | } 90 | 91 | impl Serialize for EqAffine { 92 | fn serialize(&self, s: S) -> Result { 93 | serialize_bytes(self.to_bytes(), s) 94 | } 95 | } 96 | 97 | impl<'de> Deserialize<'de> for EqAffine { 98 | fn deserialize>(d: D) -> Result { 99 | let bytes = deserialize_bytes(d)?; 100 | match EqAffine::from_bytes(&bytes).into() { 101 | Some(eq_affine) => Ok(eq_affine), 102 | None => Err(D::Error::custom( 103 | "deserialized bytes don't encode a Vesta curve point", 104 | )), 105 | } 106 | } 107 | } 108 | 109 | impl Serialize for Ep { 110 | fn serialize(&self, s: S) -> Result { 111 | EpAffine::serialize(&self.to_affine(), s) 112 | } 113 | } 114 | 115 | impl<'de> Deserialize<'de> for Ep { 116 | fn deserialize>(d: D) -> Result { 117 | Ok(Self::from(EpAffine::deserialize(d)?)) 118 | } 119 | } 120 | 121 | impl Serialize for Eq { 122 | fn serialize(&self, s: S) -> Result { 123 | EqAffine::serialize(&self.to_affine(), s) 124 | } 125 | } 126 | 127 | impl<'de> Deserialize<'de> for Eq { 128 | fn deserialize>(d: D) -> Result { 129 | Ok(Self::from(EqAffine::deserialize(d)?)) 130 | } 131 | } 132 | 133 | #[cfg(test)] 134 | mod tests { 135 | use super::*; 136 | 137 | use core::fmt::Debug; 138 | 139 | use ff::Field; 140 | use group::{prime::PrimeCurveAffine, Curve, Group}; 141 | use rand::SeedableRng; 142 | use rand_xorshift::XorShiftRng; 143 | 144 | use crate::curves::{Ep, Eq}; 145 | 146 | fn test_roundtrip Deserialize<'a> + Debug + PartialEq>(t: &T) { 147 | let serialized_json = serde_json::to_vec(t).unwrap(); 148 | assert_eq!(*t, serde_json::from_slice(&serialized_json).unwrap()); 149 | 150 | let serialized_bincode = bincode::serialize(t).unwrap(); 151 | assert_eq!(*t, bincode::deserialize(&serialized_bincode).unwrap()); 152 | } 153 | 154 | #[test] 155 | fn serde_fp() { 156 | let mut rng = XorShiftRng::from_seed([ 157 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 158 | 0xbc, 0xe5, 159 | ]); 160 | 161 | for _ in 0..100 { 162 | let f = Fp::random(&mut rng); 163 | test_roundtrip(&f); 164 | } 165 | 166 | let f = Fp::zero(); 167 | test_roundtrip(&f); 168 | assert_eq!( 169 | serde_json::from_slice::( 170 | br#""0000000000000000000000000000000000000000000000000000000000000000""# 171 | ) 172 | .unwrap(), 173 | f 174 | ); 175 | assert_eq!( 176 | bincode::deserialize::(&[ 177 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 178 | 0, 0, 0, 0 179 | ]) 180 | .unwrap(), 181 | f 182 | ); 183 | 184 | let f = Fp::one(); 185 | test_roundtrip(&f); 186 | assert_eq!( 187 | serde_json::from_slice::( 188 | br#""0100000000000000000000000000000000000000000000000000000000000000""# 189 | ) 190 | .unwrap(), 191 | f 192 | ); 193 | assert_eq!( 194 | bincode::deserialize::(&[ 195 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 196 | 0, 0, 0, 0 197 | ]) 198 | .unwrap(), 199 | f 200 | ); 201 | } 202 | 203 | #[test] 204 | fn serde_fq() { 205 | let mut rng = XorShiftRng::from_seed([ 206 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 207 | 0xbc, 0xe5, 208 | ]); 209 | 210 | for _ in 0..100 { 211 | let f = Fq::random(&mut rng); 212 | test_roundtrip(&f); 213 | } 214 | 215 | let f = Fq::zero(); 216 | test_roundtrip(&f); 217 | assert_eq!( 218 | serde_json::from_slice::( 219 | br#""0000000000000000000000000000000000000000000000000000000000000000""# 220 | ) 221 | .unwrap(), 222 | f 223 | ); 224 | assert_eq!( 225 | bincode::deserialize::(&[ 226 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 227 | 0, 0, 0, 0 228 | ]) 229 | .unwrap(), 230 | f 231 | ); 232 | 233 | let f = Fq::one(); 234 | test_roundtrip(&f); 235 | assert_eq!( 236 | serde_json::from_slice::( 237 | br#""0100000000000000000000000000000000000000000000000000000000000000""# 238 | ) 239 | .unwrap(), 240 | f 241 | ); 242 | assert_eq!( 243 | bincode::deserialize::(&[ 244 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 245 | 0, 0, 0, 0 246 | ]) 247 | .unwrap(), 248 | f 249 | ); 250 | } 251 | 252 | #[test] 253 | fn serde_ep_affine() { 254 | let mut rng = XorShiftRng::from_seed([ 255 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 256 | 0xbc, 0xe5, 257 | ]); 258 | 259 | for _ in 0..100 { 260 | let f = Ep::random(&mut rng); 261 | test_roundtrip(&f.to_affine()); 262 | } 263 | 264 | let f = EpAffine::identity(); 265 | test_roundtrip(&f); 266 | assert_eq!( 267 | serde_json::from_slice::( 268 | br#""0000000000000000000000000000000000000000000000000000000000000000""# 269 | ) 270 | .unwrap(), 271 | f 272 | ); 273 | assert_eq!( 274 | bincode::deserialize::(&[ 275 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 276 | 0, 0, 0, 0 277 | ]) 278 | .unwrap(), 279 | f 280 | ); 281 | 282 | let f = EpAffine::generator(); 283 | test_roundtrip(&f); 284 | assert_eq!( 285 | serde_json::from_slice::( 286 | br#""00000000ed302d991bf94c09fc98462200000000000000000000000000000040""# 287 | ) 288 | .unwrap(), 289 | f 290 | ); 291 | assert_eq!( 292 | bincode::deserialize::(&[ 293 | 0, 0, 0, 0, 237, 48, 45, 153, 27, 249, 76, 9, 252, 152, 70, 34, 0, 0, 0, 0, 0, 0, 294 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 295 | ]) 296 | .unwrap(), 297 | f 298 | ); 299 | } 300 | 301 | #[test] 302 | fn serde_eq_affine() { 303 | let mut rng = XorShiftRng::from_seed([ 304 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 305 | 0xbc, 0xe5, 306 | ]); 307 | 308 | for _ in 0..100 { 309 | let f = Eq::random(&mut rng); 310 | test_roundtrip(&f.to_affine()); 311 | } 312 | 313 | let f = EqAffine::identity(); 314 | test_roundtrip(&f); 315 | assert_eq!( 316 | serde_json::from_slice::( 317 | br#""0000000000000000000000000000000000000000000000000000000000000000""# 318 | ) 319 | .unwrap(), 320 | f 321 | ); 322 | assert_eq!( 323 | bincode::deserialize::(&[ 324 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 325 | 0, 0, 0, 0 326 | ]) 327 | .unwrap(), 328 | f 329 | ); 330 | 331 | let f = EqAffine::generator(); 332 | test_roundtrip(&f); 333 | assert_eq!( 334 | serde_json::from_slice::( 335 | br#""0000000021eb468cdda89409fc98462200000000000000000000000000000040""# 336 | ) 337 | .unwrap(), 338 | f 339 | ); 340 | assert_eq!( 341 | bincode::deserialize::(&[ 342 | 0, 0, 0, 0, 33, 235, 70, 140, 221, 168, 148, 9, 252, 152, 70, 34, 0, 0, 0, 0, 0, 0, 343 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 344 | ]) 345 | .unwrap(), 346 | f 347 | ); 348 | } 349 | 350 | #[test] 351 | fn serde_ep() { 352 | let mut rng = XorShiftRng::from_seed([ 353 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 354 | 0xbc, 0xe5, 355 | ]); 356 | 357 | for _ in 0..100 { 358 | let f = Ep::random(&mut rng); 359 | test_roundtrip(&f); 360 | } 361 | 362 | let f = Ep::identity(); 363 | test_roundtrip(&f); 364 | assert_eq!( 365 | serde_json::from_slice::( 366 | br#""0000000000000000000000000000000000000000000000000000000000000000""# 367 | ) 368 | .unwrap(), 369 | f 370 | ); 371 | assert_eq!( 372 | bincode::deserialize::(&[ 373 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 374 | 0, 0, 0, 0 375 | ]) 376 | .unwrap(), 377 | f 378 | ); 379 | 380 | let f = Ep::generator(); 381 | test_roundtrip(&f); 382 | assert_eq!( 383 | serde_json::from_slice::( 384 | br#""00000000ed302d991bf94c09fc98462200000000000000000000000000000040""# 385 | ) 386 | .unwrap(), 387 | f 388 | ); 389 | assert_eq!( 390 | bincode::deserialize::(&[ 391 | 0, 0, 0, 0, 237, 48, 45, 153, 27, 249, 76, 9, 252, 152, 70, 34, 0, 0, 0, 0, 0, 0, 392 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 393 | ]) 394 | .unwrap(), 395 | f 396 | ); 397 | } 398 | 399 | #[test] 400 | fn serde_eq() { 401 | let mut rng = XorShiftRng::from_seed([ 402 | 0x59, 0x62, 0xbe, 0x5d, 0x76, 0x3d, 0x31, 0x8d, 0x17, 0xdb, 0x37, 0x32, 0x54, 0x06, 403 | 0xbc, 0xe5, 404 | ]); 405 | 406 | for _ in 0..100 { 407 | let f = Eq::random(&mut rng); 408 | test_roundtrip(&f); 409 | } 410 | 411 | let f = Eq::identity(); 412 | test_roundtrip(&f); 413 | assert_eq!( 414 | serde_json::from_slice::( 415 | br#""0000000000000000000000000000000000000000000000000000000000000000""# 416 | ) 417 | .unwrap(), 418 | f 419 | ); 420 | assert_eq!( 421 | bincode::deserialize::(&[ 422 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 423 | 0, 0, 0, 0 424 | ]) 425 | .unwrap(), 426 | f 427 | ); 428 | 429 | let f = Eq::generator(); 430 | test_roundtrip(&f); 431 | assert_eq!( 432 | serde_json::from_slice::( 433 | br#""0000000021eb468cdda89409fc98462200000000000000000000000000000040""# 434 | ) 435 | .unwrap(), 436 | f 437 | ); 438 | assert_eq!( 439 | bincode::deserialize::(&[ 440 | 0, 0, 0, 0, 33, 235, 70, 140, 221, 168, 148, 9, 252, 152, 70, 34, 0, 0, 0, 0, 0, 0, 441 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 64 442 | ]) 443 | .unwrap(), 444 | f 445 | ); 446 | } 447 | } 448 | -------------------------------------------------------------------------------- /src/vesta.rs: -------------------------------------------------------------------------------- 1 | //! The Vesta and iso-Vesta elliptic curve groups. 2 | 3 | use super::{Eq, EqAffine, Fp, Fq}; 4 | 5 | /// The base field of the Vesta and iso-Vesta curves. 6 | pub type Base = Fq; 7 | 8 | /// The scalar field of the Vesta and iso-Vesta curves. 9 | pub type Scalar = Fp; 10 | 11 | /// A Vesta point in the projective coordinate space. 12 | pub type Point = Eq; 13 | 14 | /// A Vesta point in the affine coordinate space (or the point at infinity). 15 | pub type Affine = EqAffine; 16 | 17 | #[cfg(feature = "alloc")] 18 | #[test] 19 | fn test_map_to_curve_simple_swu() { 20 | use crate::arithmetic::CurveExt; 21 | use crate::curves::IsoEq; 22 | use crate::hashtocurve::map_to_curve_simple_swu; 23 | 24 | // The zero input is a special case. 25 | let p: IsoEq = map_to_curve_simple_swu::(&Fq::zero(), Eq::THETA, Eq::Z); 26 | let (x, y, z) = p.jacobian_coordinates(); 27 | 28 | assert!( 29 | format!("{:?}", x) == "0x2ccc4c6ec2660e5644305bc52527d904d408f92407f599df8f158d50646a2e78" 30 | ); 31 | assert!( 32 | format!("{:?}", y) == "0x29a34381321d13d72d50b6b462bb4ea6a9e47393fa28a47227bf35bc0ee7aa59" 33 | ); 34 | assert!( 35 | format!("{:?}", z) == "0x0b851e9e579403a76df1100f556e1f226e5656bdf38f3bf8601d8a3a9a15890b" 36 | ); 37 | 38 | let p: IsoEq = map_to_curve_simple_swu::(&Fq::one(), Eq::THETA, Eq::Z); 39 | let (x, y, z) = p.jacobian_coordinates(); 40 | 41 | assert!( 42 | format!("{:?}", x) == "0x165f8b71841c5abc3d742ec13fb16f099d596b781e6f5c7d0b6682b1216a8258" 43 | ); 44 | assert!( 45 | format!("{:?}", y) == "0x0dadef21de74ed7337a37dd74f126a92e4df73c3a704da501e36eaf59cf03120" 46 | ); 47 | assert!( 48 | format!("{:?}", z) == "0x0a3d6f6c1af02bd9274cc0b80129759ce77edeef578d7de968d4a47d39026c82" 49 | ); 50 | } 51 | 52 | #[cfg(feature = "alloc")] 53 | #[test] 54 | fn test_hash_to_curve() { 55 | use crate::arithmetic::CurveExt; 56 | 57 | // This test vector is chosen so that the first map_to_curve_simple_swu takes the gx1 non-square 58 | // "branch" and the second takes the gx1 square "branch" (opposite to the Pallas test vector). 59 | let hash = Point::hash_to_curve("z.cash:test"); 60 | let p: Point = hash(b"hello"); 61 | let (x, y, z) = p.jacobian_coordinates(); 62 | 63 | assert!( 64 | format!("{:?}", x) == "0x12763505036e0e1a6684b7a7d8d5afb7378cc2b191a95e34f44824a06fcbd08e" 65 | ); 66 | assert!( 67 | format!("{:?}", y) == "0x0256eafc0188b79bfa7c4b2b393893ddc298e90da500fa4a9aee17c2ea4240e6" 68 | ); 69 | assert!( 70 | format!("{:?}", z) == "0x1b58d4aa4d68c3f4d9916b77c79ff9911597a27f2ee46244e98eb9615172d2ad" 71 | ); 72 | } 73 | --------------------------------------------------------------------------------