├── .config └── nextest.toml ├── .github └── workflows │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.toml ├── README.md ├── benches └── sqrt.rs ├── proptest-regressions ├── fields │ ├── fp │ │ └── arkworks.txt │ ├── fq │ │ └── arkworks.txt │ ├── fr │ │ └── arkworks.txt │ └── u64 │ │ └── fp │ │ └── arkworks.txt └── invsqrt.txt ├── ristretto.sage ├── sqrt_alg.sage ├── src ├── ark_curve │ ├── bls12_377.rs │ ├── constants.rs │ ├── edwards.rs │ ├── element.rs │ ├── element │ │ ├── affine.rs │ │ └── projective.rs │ ├── elligator.rs │ ├── encoding.rs │ ├── invsqrt.rs │ ├── mod.rs │ ├── on_curve.rs │ ├── ops.rs │ ├── ops │ │ ├── affine.rs │ │ └── projective.rs │ ├── r1cs.rs │ ├── r1cs │ │ ├── element.rs │ │ ├── fqvar_ext.rs │ │ ├── inner.rs │ │ ├── lazy.rs │ │ └── ops.rs │ ├── rand.rs │ └── serialize.rs ├── error.rs ├── fields.rs ├── fields │ ├── fp.rs │ ├── fp │ │ ├── arkworks.rs │ │ ├── ops.rs │ │ ├── u32.rs │ │ ├── u32 │ │ │ ├── fiat.rs │ │ │ └── wrapper.rs │ │ ├── u64.rs │ │ └── u64 │ │ │ └── wrapper.rs │ ├── fq.rs │ ├── fq │ │ ├── arkworks.rs │ │ ├── ops.rs │ │ ├── u32.rs │ │ ├── u32 │ │ │ ├── fiat.rs │ │ │ └── wrapper.rs │ │ ├── u64.rs │ │ └── u64 │ │ │ └── wrapper.rs │ ├── fr.rs │ └── fr │ │ ├── arkworks.rs │ │ ├── ops.rs │ │ ├── u32.rs │ │ ├── u32 │ │ ├── fiat.rs │ │ └── wrapper.rs │ │ ├── u64.rs │ │ └── u64 │ │ └── wrapper.rs ├── lib.rs ├── min_curve │ ├── constants.rs │ ├── element.rs │ ├── encoding.rs │ ├── invsqrt.rs │ ├── mod.rs │ └── ops.rs └── sign.rs ├── tests ├── encoding.proptest-regressions ├── encoding.rs ├── groth16_gadgets.proptest-regressions ├── groth16_gadgets.rs ├── operations.rs └── test_vectors │ ├── add_assign_add_pk.bin │ ├── add_assign_add_vk.param │ ├── compression_pk.bin │ ├── compression_vk.param │ ├── decompression_pk.bin │ ├── decompression_vk.param │ ├── discrete_log_pk.bin │ ├── discrete_log_vk.param │ ├── elligator_pk.bin │ ├── elligator_vk.param │ ├── negation_pk.bin │ ├── negation_vk.param │ ├── public_element_input_pk.bin │ └── public_element_input_vk.param └── utils ├── field_properties.py ├── frobenius_coeff_fp12_c1.txt ├── frobenius_coeff_fp6_c1.txt ├── frobenius_coeff_fp6_c2.txt └── g2_generator.txt /.config/nextest.toml: -------------------------------------------------------------------------------- 1 | [profile.ci] 2 | # Using recommended settings from https://nexte.st/book/configuration.html#profiles 3 | failure-output = "immediate-final" 4 | fail-fast = false 5 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | on: [pull_request] 2 | 3 | name: Rust CI 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | - name: Install rust toolchain 12 | uses: dtolnay/rust-toolchain@stable 13 | - name: Load rust cache 14 | uses: astriaorg/buildjet-rust-cache@v2.5.1 15 | - name: Run cargo check, failing on warnings 16 | run: cargo check --profile ci --all-targets 17 | # It'd be nice to fail on warnings, but we're not ready for that. 18 | # env: 19 | # RUSTFLAGS: "-D warnings" 20 | 21 | test: 22 | name: Test Suite 23 | runs-on: buildjet-16vcpu-ubuntu-2204 24 | # We want to run the tests twice, once with the 25 | # default backend (u64) and one with the u32_backend, 26 | # in both cases with the r1cs feature enabled. 27 | strategy: 28 | matrix: 29 | backend: ["r1cs", "r1cs,u32_backend"] 30 | steps: 31 | - uses: actions/checkout@v4 32 | - name: Install rust toolchain 33 | uses: dtolnay/rust-toolchain@stable 34 | - name: Install nextest 35 | uses: taiki-e/install-action@nextest 36 | - name: Load rust cache 37 | uses: astriaorg/buildjet-rust-cache@v2.5.1 38 | - name: Run tests with nextest 39 | run: cargo nextest run --cargo-profile ci --profile ci --features ${{ matrix.backend }} 40 | env: 41 | CARGO_TERM_COLOR: always 42 | 43 | build_no_alloc: 44 | name: build without alloc 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v4 48 | - uses: actions-rs/toolchain@v1 49 | with: 50 | profile: minimal 51 | toolchain: stable 52 | override: true 53 | - run: rustup target add thumbv7em-none-eabihf 54 | - uses: Swatinem/rust-cache@v1 55 | - uses: actions-rs/cargo@v1 56 | with: 57 | command: build 58 | args: --target thumbv7em-none-eabihf --no-default-features 59 | 60 | fmt: 61 | name: Rustfmt 62 | runs-on: ubuntu-latest 63 | steps: 64 | - uses: actions/checkout@v4 65 | - uses: actions-rs/toolchain@v1 66 | with: 67 | profile: minimal 68 | toolchain: stable 69 | override: true 70 | - uses: Swatinem/rust-cache@v1 71 | - run: rustup component add rustfmt 72 | - uses: actions-rs/cargo@v1 73 | with: 74 | command: fmt 75 | args: --all -- --check 76 | 77 | no-std: 78 | name: no_std compatibility check 79 | runs-on: ubuntu-latest 80 | steps: 81 | - uses: actions/checkout@v4 82 | - uses: actions-rs/toolchain@v1 83 | with: 84 | profile: minimal 85 | toolchain: stable 86 | override: true 87 | - uses: Swatinem/rust-cache@v1 88 | - uses: actions-rs/cargo@v1 89 | with: 90 | command: check 91 | args: --no-default-features 92 | - uses: actions-rs/cargo@v1 93 | with: 94 | command: build 95 | args: --no-default-features 96 | - uses: actions-rs/cargo@v1 97 | with: 98 | command: test 99 | args: --no-default-features 100 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # 0.1.0 2 | 3 | * Initial release. 4 | 5 | # 0.1.1 6 | 7 | * Fix: Disable `print-trace` feature in R1CS gadgets. 8 | 9 | # 0.2.0 10 | 11 | * Fix: Use compressed representation for public elements in-circuit. 12 | 13 | # 0.3.0 14 | 15 | * Disable internal parallelism. 16 | 17 | # 0.4.0 18 | 19 | * Upgrade to 0.4.x series of Arkworks dependencies. 20 | 21 | # 0.5.0 22 | 23 | * Audit followup: 24 | * Optimization: remove 7 unnecessary constraints in the R1CS sign checks. 25 | * Fix: Resolve incorrect handling of zero in point decompression in R1CS. 26 | 27 | # 0.6.0 28 | 29 | * Fix: Resolve incorrect `SubAssign` and `AddAssign` implementations in R1CS. 30 | 31 | # 0.7.0 32 | 33 | * Fix: Add `no_std` compatibility. 34 | 35 | # 0.8.0 36 | 37 | * Add fiat-generated finite field implementations by @hdevalence in #64 38 | * refactor(arkworks): feature-gated Arkworks compatibility by @TalDerei in #67 39 | * Implement Bls12377 using our own backend by @cronokirby in #71 40 | * ci: add job to test u32_backend by @redshiftzero in #72 41 | * Arkworks feature gating by @cronokirby in #73 42 | * Implement traits in a no_std context when possible by @cronokirby in #74 43 | * Implement the start of a minimal curve implementation by @cronokirby in #75 44 | * ci: add job building with no alloc feature by @redshiftzero in #76 45 | * arkworks independent projective arithmetic ops by @redshiftzero in #77 46 | * Make modular reduction work for large byte sizes by @cronokirby in #78 47 | * Implement FromStr for all the fields by @cronokirby in #79 48 | * Implement a checked conversion from bytes method in Fq by @cronokirby in #81 49 | * arkworks-independent sqrts, point encoding/decoding by @cronokirby in #80 50 | * ci: use larger runners by @conorsch in #83 51 | * ci: dedicated profile for release + debug_assert by @conorsch in #84 52 | * rearranging arkworks / non-arkworks ECC code by @redshiftzero in #82 53 | 54 | # 0.9.0 55 | 56 | * Make raw constructors of field elements private by @cronokirby in #90 57 | * Add missing methods as need for integrating the latest version of this crate by @cronokirby in #91 58 | * fix: field modulus by @TalDerei in #92 59 | * adjust anyhow scope and remove unused dependencies by @neithanmo in #96 60 | * add power impl and expose `Fq::from_montgomery_limbs` by @redshiftzero in #98 61 | * Add missing conversion trait implementations by @neithanmo in #97 62 | 63 | # 0.10.0 64 | 65 | * Use Arkworks field arithmetic for the 64-bit backend by @redshiftzero in #102 66 | 67 | # 0.10.1 68 | 69 | * Fix recursion causing infinite loop in 32-bit backend * operator for Element 70 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "decaf377" 3 | version = "0.10.1" 4 | authors = [ 5 | "Henry de Valence ", 6 | "redshiftzero ", 7 | ] 8 | description = "A prime-order group designed for use in SNARKs over BLS12-377" 9 | edition = "2021" 10 | license = "MIT OR Apache-2.0" 11 | 12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 13 | 14 | [dependencies] 15 | # no-alloc, no-std 16 | cfg-if = "1.0" 17 | hex = { version = "=0.4.3", default-features = false } 18 | subtle = { version = "2.5", default-features = false } 19 | rand_core = { version = "0.6", default-features = false } 20 | zeroize = { version = "1.7", default-features = false } 21 | # no-std 22 | num-bigint = { version = "0.4.4", optional = true, default-features = false } 23 | # std 24 | hashbrown = { version = "0.14.3", optional = true } 25 | ark-relations = { version = "0.4", optional = true } 26 | ark-r1cs-std = { version = "0.4", optional = true } 27 | ark-std = { version = "0.4", optional = true } 28 | ark-ec = { version = "0.4", optional = true } 29 | ark-ff = { version = "0.4", optional = true } 30 | ark-serialize = { version = "0.4", optional = true } 31 | ark-bls12-377 = { version = "0.4", optional = true } 32 | ark-ed-on-bls12-377 = { version = "0.4", optional = true } 33 | ark-groth16 = { version = "0.4", optional = true } 34 | ark-snark = { version = "0.4", optional = true } 35 | once_cell = { version = "1.8", optional = true, default-features = false } 36 | 37 | # This matches what ark-std (a library for no_std compatibility) does, having 38 | # a default feature of std - without the ark-std std feature, decaf377 doesn't 39 | # compile 40 | [features] 41 | default = ["arkworks"] 42 | alloc = ["once_cell/alloc", "zeroize/alloc", "rand_core/alloc"] 43 | std = [ 44 | "alloc", 45 | "zeroize/std", 46 | "once_cell/std", 47 | "num-bigint/std", 48 | "hex/std", 49 | "subtle/std", 50 | "rand_core/std" 51 | ] 52 | parallel = [ 53 | "ark-ff/parallel", 54 | "ark-ec/parallel", 55 | "ark-groth16/parallel", 56 | "ark-std/parallel", 57 | "ark-r1cs-std/parallel", 58 | ] 59 | # TODO: eventually, feature-gate all arkworks deps behind this feature. 60 | arkworks = [ 61 | "std", 62 | "ark-std", 63 | "ark-ec", 64 | "ark-ff", 65 | "ark-serialize", 66 | "ark-bls12-377", 67 | "ark-ed-on-bls12-377", 68 | "hashbrown", 69 | ] 70 | r1cs = ["arkworks", "ark-groth16", "ark-r1cs-std", "ark-relations", "ark-snark"] 71 | u32_backend = [] 72 | 73 | [dev-dependencies] 74 | proptest = "1" 75 | criterion = { version = "0.3", features = ["html_reports"] } 76 | rand_core = { version = "0.6.3", features = ["getrandom"] } 77 | rand_chacha = "0.3" 78 | anyhow = { version = "1.0" } 79 | 80 | [[test]] 81 | name = "encoding" 82 | required-features = ["arkworks"] 83 | 84 | [[test]] 85 | name = "groth16_gadgets" 86 | required-features = ["r1cs"] 87 | 88 | [[test]] 89 | name = "operations" 90 | required-features = ["arkworks"] 91 | 92 | [[bench]] 93 | name = "sqrt" 94 | harness = false 95 | required-features = ["arkworks"] 96 | 97 | # Create profile for running checks in CI that are mostly "release" mode, 98 | # but also checking the `debug_assert `lines. 99 | [profile.ci] 100 | inherits = "release" 101 | debug-assertions = true 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # decaf377 2 | 3 | [![Crates.io][crates-badge]][crates-url] 4 | 5 | [crates-badge]: https://img.shields.io/crates/v/decaf377.svg 6 | [crates-url]: https://crates.io/crates/decaf377 7 | 8 | Many zero-knowledge protocols require a cryptographic group that can be used 9 | inside of an arithmetic circuit. This is accomplished by defining an “embedded” 10 | elliptic curve whose base field is the scalar field of the proving curve used 11 | by the proof system. 12 | 13 | The Zexe paper, which defined BLS12-377, also defined (but did not name) a 14 | cofactor-4 Edwards curve defined over the BLS12-377 scalar field for exactly 15 | this purpose. However, non-prime-order groups are a leaky abstraction, forcing 16 | all downstream constructions to pay attention to correct handling of the 17 | cofactor. Although it is usually possible to do so safely, it requires 18 | additional care, and as discussed below, the optimal technique for handling the 19 | cofactor is different inside and outside of a circuit. 20 | 21 | Instead, applying the Decaf construction to this curve gives decaf377, a clean 22 | abstraction that provides a prime-order group, complete with hash-to-group 23 | functionality, and works the same way inside and outside of a circuit. 24 | 25 | More details are available on the [Penumbra 26 | website](https://protocol.penumbra.zone/main/crypto/decaf377.html). 27 | 28 | ## Features 29 | 30 | * `std`: default, for use in `std` environments, 31 | * `alloc`: default, for use in `alloc` environments, 32 | * `arkworks`: default, uses Arkworks crates for elliptic curve operations, 33 | * `u32_backend`: uses 32-bit finite field arithmetic (default is 64-bit), 34 | * `r1cs`: enables rank-1 constraint system gadgets, 35 | * `parallel`: enables the use of parallelism. 36 | 37 | ## Benchmarks 38 | 39 | Run `criterion` benchmarks using: 40 | 41 | ``` 42 | cargo bench 43 | ``` 44 | 45 | This will generate a report at `target/criterion/report/index.html`. 46 | 47 | ## Generate Test Vectors 48 | 49 | There is a test utility that will generate the proving and verifying keys for 50 | the R1CS tests. These keys are pre-generated to guard against breaking changes 51 | to the circuits (e.g. when upgrading Arkworks dependencies). 52 | 53 | ``` 54 | cargo test generate_test_vectors -- --ignored 55 | ``` 56 | -------------------------------------------------------------------------------- /benches/sqrt.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::{Field, Zero}; 2 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 3 | use decaf377::Fq; 4 | use decaf377::ZETA; 5 | use rand_chacha::ChaChaRng; 6 | use rand_core::{RngCore, SeedableRng}; 7 | 8 | fn sqrt_original(u: &Fq, v: &Fq) -> (bool, Fq) { 9 | if u.is_zero() { 10 | return (true, *u); 11 | } 12 | if v.is_zero() { 13 | return (false, *v); 14 | } 15 | 16 | let uv = v.inverse().expect("nonzero") * u; 17 | if let Some(sqrt_uv) = uv.sqrt() { 18 | (true, sqrt_uv) 19 | } else { 20 | let sqrt_zeta_uv = (ZETA * uv).sqrt().expect("must be square if u/v nonsquare"); 21 | (false, sqrt_zeta_uv) 22 | } 23 | } 24 | 25 | pub fn bench_sqrt_ratio(c: &mut Criterion) { 26 | let mut group = c.benchmark_group("sqrt"); 27 | let n = 10; 28 | let mut rng = ChaChaRng::seed_from_u64(666); 29 | let mut test_field_elements = Vec::with_capacity(n); 30 | for _ in 0..n { 31 | let mut i_bytes = [0u8; 32]; 32 | let mut j_bytes = [0u8; 32]; 33 | rng.fill_bytes(&mut i_bytes); 34 | rng.fill_bytes(&mut j_bytes); 35 | test_field_elements.push(( 36 | Fq::from_le_bytes_mod_order(&i_bytes[..]), 37 | Fq::from_le_bytes_mod_order(&j_bytes[..]), 38 | )) 39 | } 40 | 41 | for (i, j) in test_field_elements { 42 | group.bench_with_input( 43 | BenchmarkId::new("Arkworks", format!("{}/{}", i, j)), 44 | &(&i, &j), 45 | |b, (i, j)| b.iter(|| sqrt_original(i, j)), 46 | ); 47 | group.bench_with_input( 48 | BenchmarkId::new("Sarkar2020", format!("{}/{}", i, j)), 49 | &(&i, &j), 50 | |b, (i, j)| b.iter(|| Fq::sqrt_ratio_zeta(i, j)), 51 | ); 52 | } 53 | group.finish(); 54 | } 55 | 56 | criterion_group!(benches, bench_sqrt_ratio); 57 | criterion_main!(benches); 58 | -------------------------------------------------------------------------------- /proptest-regressions/fields/fp/arkworks.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc cdd619576f50197f2531266a43db3be354532b33cd74b87be7ca92df4a5c0715 # shrinks to a = Fp(0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000) 8 | -------------------------------------------------------------------------------- /proptest-regressions/fields/fq/arkworks.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc a91952dc19f9fe127f1a6f406400574aaedfa9017cd9010ddc509b26baa847fc # shrinks to bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 8 | -------------------------------------------------------------------------------- /proptest-regressions/fields/fr/arkworks.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 77fe8cd73668f517c0ddfd13999e303d1121ad67db8c896f6d7f33195288d791 # shrinks to a = Fr(0x0000000000000001000000000000000000000000000000000000000000000000) 8 | cc 82d116306befe522f9442271ef7cf8675f016da4d94c21e2fb631fd544187bf6 # shrinks to a = Fr(0x0000000000000000000000000000000000000000000000000000000000000000) 9 | -------------------------------------------------------------------------------- /proptest-regressions/fields/u64/fp/arkworks.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 9373fd2a7fc4c09fde3e7bf317b5a2d3c386d7b02761985e4b1143d1e2b6ee64 # shrinks to a = Fp(0x3a4ec137358eb353d51d94f3b6f7a23aade5b83af2b2c15678cbf07ff973bcb0d33ac4e08fe7b3c930f2f5db300db100) 8 | -------------------------------------------------------------------------------- /proptest-regressions/invsqrt.txt: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc c12e8e3a48667a0687a3eeeb95dad15445aab8ab494970b3055df6fb351a3d5a # shrinks to u = Fq(0x0100000000000000000000000000000000000000000000000000000000000000), v = Fq(0x0100000000000000000000000000000000000000000000000000000000000000) 8 | -------------------------------------------------------------------------------- /sqrt_alg.sage: -------------------------------------------------------------------------------- 1 | p = 8444461749428370424248824938781546531375899335154063827935233455917409239041 2 | F = GF(p) 3 | z = F(2841681278031794617739547238867782961338435681360110683443920362658525667816) 4 | # First, solve p - 1 = 2**n * m where n >= 1 and m odd 5 | n = 47 6 | m = (p - 1) / 2**n 7 | 8 | # Next, compute g = z^m where z is a quadratic non-residue in Fp 9 | g = z ** m 10 | 11 | # Finally, define l_0... l_{k-1} > 0 such that l_0 + .. + l_{k-1} = n-1 12 | k = 6 13 | l = [7, 7, 8, 8, 8, 8] 14 | w = 8 15 | 16 | # g powers lookup table: Indexed by power of two, then nu 17 | powers_needed_for_t_i = [0, 8, 16, 24, 32] 18 | powers_needed_for_t_k_over_2 = [40] 19 | # This is Table 1 and 2 from Sarkar 2020 (combined) 20 | gtab = {} 21 | for power_of_two in powers_needed_for_t_i + powers_needed_for_t_k_over_2: 22 | gtab_i = [] 23 | for nu in range(0, 2**w): 24 | exponent = nu * F(2) ** power_of_two 25 | gtab_i.append(g**exponent) 26 | gtab[power_of_two] = gtab_i 27 | 28 | # s lookup table: Indexed by g^{\nu * 2^{n-l}} 29 | # This is Table 3 from Sarkar 2020 30 | s_lookup_table = {} 31 | for nu in range(0, 2**w): 32 | s_lookup_table[g**(-1 * nu * 2**(n-w))] = nu 33 | 34 | 35 | def sqrt_ratio(num, den): 36 | s = den**(2**n - 1) 37 | t = s**2 * den 38 | w = (num * t)**((m-1)/2) * s 39 | 40 | v = w * den; 41 | uv = w * num; 42 | 43 | assert v == (num / den)**((m - 1) / 2) 44 | assert uv == (num / den)**((m + 1) / 2) 45 | 46 | return table_based_findSqRoot_sarkar(uv, v) 47 | 48 | 49 | def table_based_findSqRoot_sarkar(uv, v): 50 | """Implements decaf377 Sarkar 2020 based method""" 51 | x = uv * v # Such that x = u**m 52 | 53 | x5 = x 54 | x4 = x5 ** (2**l[5]) 55 | x3 = x4 ** (2**l[4]) 56 | x2 = x3 ** (2**l[3]) 57 | x1 = x2 ** (2**l[2]) 58 | x0 = x1 ** (2**l[1]) 59 | 60 | # i = 0 61 | # Here we want to look up s_0 = q_0 * g**{n-7}, but our table has 62 | # powers of g**{n-8}, so we're actually looking up q_prime = 2*q_0. 63 | q_0_prime = s_lookup_table[x0] # Since x0 = alpha0 64 | t = q_0_prime 65 | 66 | # i = 1 67 | alpha_1 = x1 * gtab[32][t] 68 | #breakpoint() 69 | q_1_prime = s_lookup_table[alpha_1] 70 | # The left shift values come from the final expression for t: 71 | # t_6 = q_0_prime + q_1_prime * 2^7 + ... + q_5 * 2^39 72 | t += q_1_prime << 7 73 | 74 | # i = 2 75 | alpha_2 = x2 * gtab[24][t & 0xFF] * gtab[32][t >> 8] 76 | assert (t >> 8) & 0xFF == q_1_prime // 2 77 | q_2 = s_lookup_table[alpha_2] 78 | t += q_2 << 15 79 | 80 | # i = 3 81 | alpha_3 = x3 * gtab[16][t & 0xFF] * gtab[24][(t >> 8) & 0xFF] * gtab[32][(t >> 16) & 0xFF] 82 | assert (t >> 16) & 0xFF == q_2 // 2 83 | q_3 = s_lookup_table[alpha_3] 84 | t += q_3 << 23 85 | 86 | # i = 4 87 | alpha_4 = x4 * gtab[8][t & 0xFF] * gtab[16][(t >> 8) & 0xFF] * gtab[24][(t >> 16) & 0xFF] * gtab[32][(t >> 24) & 0xFF] 88 | assert (t >> 24) & 0xFF == q_3 // 2 89 | q_4 = s_lookup_table[alpha_4] 90 | t += q_4 << 31 91 | 92 | # i = 5 93 | alpha_5 = x5 * gtab[0][t & 0xFF] * gtab[8][(t >> 8) & 0xFF] * gtab[16][(t >> 16) & 0xFF] * gtab[24][(t >> 24) & 0xFF] * gtab[32][(t >> 32) & 0xFF] 94 | assert (t >> 32) & 0xFF == q_4 // 2 95 | q_5 = s_lookup_table[alpha_5] 96 | t += q_5 << 39 97 | 98 | assert t == q_0_prime + q_1_prime * 2**7 + q_2 * 2**15 + q_3 * 2**23 + q_4 * 2**31 + q_5 * 2**39 # Lemma 4 assertion 99 | 100 | t = (t + 1) >> 1; 101 | 102 | # Take 8 bits at a time, e.g. (t & 0xFF) is taking the last 8 bits of t to yield a value from 0-255, which are the allowed values in each g lookup table 103 | gamma = gtab[0][t & 0xFF] * gtab[8][(t >> 8) & 0xFF] * gtab[16][(t >> 16) & 0xFF] * gtab[24][(t >> 24) & 0xFF] * gtab[32][(t >> 32) & 0xFF] * gtab[40][(t >> 40)] 104 | y = uv * gamma 105 | 106 | if q_0_prime % 2 != 0: 107 | y *= z**((1-m) / 2) 108 | 109 | return y 110 | 111 | 112 | def test_sqrt(n): 113 | for _ in range(n): 114 | num = F.random_element() 115 | den = F(1) 116 | u = num / den 117 | 118 | res = sqrt_ratio(num, den) 119 | if u.is_square(): 120 | assert res**2 == u 121 | else: 122 | assert res**2 == u * z 123 | 124 | 125 | #test_sqrt(10000) 126 | test_sqrt(100) -------------------------------------------------------------------------------- /src/ark_curve/bls12_377.rs: -------------------------------------------------------------------------------- 1 | use crate::fields::{fp::Fp, fq::Fq}; 2 | use ark_ec::{ 3 | bls12::{Bls12, Bls12Config, TwistType}, 4 | models::short_weierstrass::SWCurveConfig, 5 | models::CurveConfig, 6 | short_weierstrass::Affine, 7 | }; 8 | use ark_ff::{fields::models::fp2::Fp2Config, Field, Fp12Config, Fp2, Fp6, Fp6Config}; 9 | 10 | pub struct F2Config; 11 | 12 | impl Fp2Config for F2Config { 13 | type Fp = Fp; 14 | 15 | const NONRESIDUE: Fp = Fp::QUADRATIC_NON_RESIDUE; 16 | 17 | const FROBENIUS_COEFF_FP2_C1: &'static [Fp] = &[Fp::ONE, Fp::MINUS_ONE]; 18 | } 19 | 20 | #[derive(Debug, Clone, Copy)] 21 | pub struct F6Config; 22 | 23 | impl Fp6Config for F6Config { 24 | type Fp2Config = F2Config; 25 | 26 | const NONRESIDUE: Fp2 = Fp2::new(Fp::ZERO, Fp::ONE); 27 | 28 | const FROBENIUS_COEFF_FP6_C1: &'static [Fp2] = &[ 29 | Fp2::new(Fp::ONE, Fp::ZERO), 30 | Fp2::new( 31 | Fp::from_montgomery_limbs([ 32 | 6382252053795993818, 33 | 1383562296554596171, 34 | 11197251941974877903, 35 | 6684509567199238270, 36 | 6699184357838251020, 37 | 19987743694136192, 38 | ]), 39 | Fp::ZERO, 40 | ), 41 | Fp2::new( 42 | Fp::from_montgomery_limbs([ 43 | 15766275933608376691, 44 | 15635974902606112666, 45 | 1934946774703877852, 46 | 18129354943882397960, 47 | 15437979634065614942, 48 | 101285514078273488, 49 | ]), 50 | Fp::ZERO, 51 | ), 52 | Fp2::new(Fp::MINUS_ONE, Fp::ZERO), 53 | Fp2::new( 54 | Fp::from_montgomery_limbs([ 55 | 3203870859294639911, 56 | 276961138506029237, 57 | 9479726329337356593, 58 | 13645541738420943632, 59 | 7584832609311778094, 60 | 101110569012358506, 61 | ]), 62 | Fp::ZERO, 63 | ), 64 | Fp2::new( 65 | Fp::from_montgomery_limbs([ 66 | 12266591053191808654, 67 | 4471292606164064357, 68 | 295287422898805027, 69 | 2200696361737783943, 70 | 17292781406793965788, 71 | 19812798628221209, 72 | ]), 73 | Fp::ZERO, 74 | ), 75 | ]; 76 | 77 | const FROBENIUS_COEFF_FP6_C2: &'static [Fp2] = &[ 78 | Fp2::new(Fp::ONE, Fp::ZERO), 79 | Fp2::new( 80 | Fp::from_montgomery_limbs([ 81 | 15766275933608376691, 82 | 15635974902606112666, 83 | 1934946774703877852, 84 | 18129354943882397960, 85 | 15437979634065614942, 86 | 101285514078273488, 87 | ]), 88 | Fp::ZERO, 89 | ), 90 | Fp2::new( 91 | Fp::from_montgomery_limbs([ 92 | 3203870859294639911, 93 | 276961138506029237, 94 | 9479726329337356593, 95 | 13645541738420943632, 96 | 7584832609311778094, 97 | 101110569012358506, 98 | ]), 99 | Fp::ZERO, 100 | ), 101 | Fp2::new(Fp::ONE, Fp::ZERO), 102 | Fp2::new( 103 | Fp::from_montgomery_limbs([ 104 | 15766275933608376691, 105 | 15635974902606112666, 106 | 1934946774703877852, 107 | 18129354943882397960, 108 | 15437979634065614942, 109 | 101285514078273488, 110 | ]), 111 | Fp::ZERO, 112 | ), 113 | Fp2::new( 114 | Fp::from_montgomery_limbs([ 115 | 3203870859294639911, 116 | 276961138506029237, 117 | 9479726329337356593, 118 | 13645541738420943632, 119 | 7584832609311778094, 120 | 101110569012358506, 121 | ]), 122 | Fp::ZERO, 123 | ), 124 | ]; 125 | } 126 | 127 | #[derive(Debug, Clone, Copy)] 128 | pub struct F12Config; 129 | 130 | impl Fp12Config for F12Config { 131 | type Fp6Config = F6Config; 132 | 133 | const NONRESIDUE: Fp6 = Fp6::new(Fp2::ZERO, Fp2::ONE, Fp2::ZERO); 134 | 135 | const FROBENIUS_COEFF_FP12_C1: &'static [Fp2] = &[ 136 | Fp2::new(Fp::ONE, Fp::ZERO), 137 | Fp2::new( 138 | Fp::from_montgomery_limbs([ 139 | 7981638599956744862, 140 | 11830407261614897732, 141 | 6308788297503259939, 142 | 10596665404780565693, 143 | 11693741422477421038, 144 | 61545186993886319, 145 | ]), 146 | Fp::ZERO, 147 | ), 148 | Fp2::new( 149 | Fp::from_montgomery_limbs([ 150 | 6382252053795993818, 151 | 1383562296554596171, 152 | 11197251941974877903, 153 | 6684509567199238270, 154 | 6699184357838251020, 155 | 19987743694136192, 156 | ]), 157 | Fp::ZERO, 158 | ), 159 | Fp2::new( 160 | Fp::from_montgomery_limbs([ 161 | 10965161018967488287, 162 | 18251363109856037426, 163 | 7036083669251591763, 164 | 16109345360066746489, 165 | 4679973768683352764, 166 | 96952949334633821, 167 | ]), 168 | Fp::ZERO, 169 | ), 170 | Fp2::new( 171 | Fp::from_montgomery_limbs([ 172 | 15766275933608376691, 173 | 15635974902606112666, 174 | 1934946774703877852, 175 | 18129354943882397960, 176 | 15437979634065614942, 177 | 101285514078273488, 178 | ]), 179 | Fp::ZERO, 180 | ), 181 | Fp2::new( 182 | Fp::from_montgomery_limbs([ 183 | 2983522419010743425, 184 | 6420955848241139694, 185 | 727295371748331824, 186 | 5512679955286180796, 187 | 11432976419915483342, 188 | 35407762340747501, 189 | ]), 190 | Fp::ZERO, 191 | ), 192 | Fp2::new(Fp::MINUS_ONE, Fp::ZERO), 193 | Fp2::new( 194 | Fp::from_montgomery_limbs([ 195 | 1604484313133888867, 196 | 8276860247155279292, 197 | 14368189973808974556, 198 | 9733385900839616209, 199 | 2590275544672608076, 200 | 59553125712608379, 201 | ]), 202 | Fp::ZERO, 203 | ), 204 | Fp2::new( 205 | Fp::from_montgomery_limbs([ 206 | 3203870859294639911, 207 | 276961138506029237, 208 | 9479726329337356593, 209 | 13645541738420943632, 210 | 7584832609311778094, 211 | 101110569012358506, 212 | ]), 213 | Fp::ZERO, 214 | ), 215 | Fp2::new( 216 | Fp::from_montgomery_limbs([ 217 | 17067705967832697058, 218 | 1855904398914139597, 219 | 13640894602060642732, 220 | 4220705945553435413, 221 | 9604043198466676350, 222 | 24145363371860877, 223 | ]), 224 | Fp::ZERO, 225 | ), 226 | Fp2::new( 227 | Fp::from_montgomery_limbs([ 228 | 12266591053191808654, 229 | 4471292606164064357, 230 | 295287422898805027, 231 | 2200696361737783943, 232 | 17292781406793965788, 233 | 19812798628221209, 234 | ]), 235 | Fp::ZERO, 236 | ), 237 | Fp2::new( 238 | Fp::from_montgomery_limbs([ 239 | 6602600494079890304, 240 | 13686311660529037330, 241 | 1502938825854351055, 242 | 14817371350334001107, 243 | 2851040547234545772, 244 | 85690550365747197, 245 | ]), 246 | Fp::ZERO, 247 | ), 248 | ]; 249 | } 250 | 251 | pub struct OurG1Config; 252 | 253 | impl CurveConfig for OurG1Config { 254 | type BaseField = Fp; 255 | 256 | type ScalarField = Fq; 257 | 258 | const COFACTOR: &'static [u64] = &[0x0, 0x170b5d4430000000]; 259 | 260 | const COFACTOR_INV: Self::ScalarField = Fq::from_montgomery_limbs([ 261 | 2013239619100046060, 262 | 4201184776506987597, 263 | 2526766393982337036, 264 | 1114629510922847535, 265 | ]); 266 | } 267 | 268 | const G1_GENERATOR_X: Fp = Fp::from_montgomery_limbs([ 269 | 2742467569752756724, 270 | 14217256487979144792, 271 | 6635299530028159197, 272 | 8509097278468658840, 273 | 14518893593143693938, 274 | 46181716169194829, 275 | ]); 276 | 277 | const G1_GENERATOR_Y: Fp = Fp::from_montgomery_limbs([ 278 | 9336971515457667571, 279 | 28021381849722296, 280 | 18085035374859187530, 281 | 14013031479170682136, 282 | 3369780711397861396, 283 | 35370409237953649, 284 | ]); 285 | 286 | impl SWCurveConfig for OurG1Config { 287 | const COEFF_A: Self::BaseField = Fp::ZERO; 288 | 289 | const COEFF_B: Self::BaseField = Fp::ONE; 290 | 291 | const GENERATOR: Affine = Affine::new_unchecked(G1_GENERATOR_X, G1_GENERATOR_Y); 292 | } 293 | 294 | pub struct OurG2Config; 295 | 296 | impl CurveConfig for OurG2Config { 297 | type BaseField = Fp2; 298 | 299 | type ScalarField = Fq; 300 | 301 | const COFACTOR: &'static [u64] = &[ 302 | 0x0000000000000001, 303 | 0x452217cc90000000, 304 | 0xa0f3622fba094800, 305 | 0xd693e8c36676bd09, 306 | 0x8c505634fae2e189, 307 | 0xfbb36b00e1dcc40c, 308 | 0xddd88d99a6f6a829, 309 | 0x26ba558ae9562a, 310 | ]; 311 | 312 | const COFACTOR_INV: Self::ScalarField = Fq::from_montgomery_limbs([ 313 | 15499857013495546999, 314 | 4613531467548868169, 315 | 14546778081091178013, 316 | 549402535258503313, 317 | ]); 318 | } 319 | 320 | pub const G2_GENERATOR_X: Fp2 = Fp2::new( 321 | Fp::from_montgomery_limbs([ 322 | 7534593107747697243, 323 | 7390176809662624395, 324 | 16990527120569264207, 325 | 2168572232730518502, 326 | 9443417493680878057, 327 | 109821976444144002, 328 | ]), 329 | Fp::from_montgomery_limbs([ 330 | 6846220294590070585, 331 | 17925825951095956135, 332 | 15355657819052935248, 333 | 16808496983586309946, 334 | 18438381910454061441, 335 | 78904498268135389, 336 | ]), 337 | ); 338 | 339 | pub const G2_GENERATOR_Y: Fp2 = Fp2::new( 340 | Fp::from_montgomery_limbs([ 341 | 15398259615690998543, 342 | 413927750809907693, 343 | 6945668964135547374, 344 | 3622202639115414553, 345 | 11542235856284301842, 346 | 111174645670174930, 347 | ]), 348 | Fp::from_montgomery_limbs([ 349 | 6296061721506977525, 350 | 16832990956758385678, 351 | 2538166719760928425, 352 | 9449086974571632418, 353 | 3122185334549858583, 354 | 25052933797626130, 355 | ]), 356 | ); 357 | 358 | impl SWCurveConfig for OurG2Config { 359 | const COEFF_A: Self::BaseField = Fp2::new(OurG1Config::COEFF_A, OurG1Config::COEFF_A); 360 | 361 | const COEFF_B: Self::BaseField = Fp2::new( 362 | Fp::ZERO, 363 | Fp::from_montgomery_limbs([ 364 | 9255502405446297221, 365 | 10229180150694123945, 366 | 9215585410771530959, 367 | 13357015519562362907, 368 | 5437107869987383107, 369 | 16259554076827459, 370 | ]), 371 | ); 372 | 373 | const GENERATOR: Affine = Affine::new_unchecked(G2_GENERATOR_X, G2_GENERATOR_Y); 374 | } 375 | 376 | /// A marker struct for our implementation of BLS12-377 over our backend fields, using Arkworks. 377 | pub struct Config; 378 | 379 | impl Bls12Config for Config { 380 | const X: &'static [u64] = &[0x8508c00000000001]; 381 | /// `x` is positive 382 | const X_IS_NEGATIVE: bool = false; 383 | 384 | const TWIST_TYPE: TwistType = TwistType::D; 385 | 386 | type Fp = Fp; 387 | 388 | type Fp2Config = F2Config; 389 | 390 | type Fp6Config = F6Config; 391 | 392 | type Fp12Config = F12Config; 393 | 394 | type G1Config = OurG1Config; 395 | 396 | type G2Config = OurG2Config; 397 | } 398 | 399 | pub type Bls12_377 = Bls12; 400 | -------------------------------------------------------------------------------- /src/ark_curve/constants.rs: -------------------------------------------------------------------------------- 1 | use once_cell::sync::Lazy; 2 | 3 | use crate::{Fq, Fr}; 4 | use ark_ff::{self, BigInteger256, Field}; 5 | 6 | use ark_ed_on_bls12_377::{Fq as ArkFq, Fr as ArkFr}; 7 | 8 | pub static ONE: Lazy = Lazy::new(|| Fq::ONE); 9 | pub static TWO: Lazy = Lazy::new(|| Fq::ONE + Fq::ONE); 10 | 11 | pub(crate) fn from_ark_fq(x: ArkFq) -> Fq { 12 | BigInteger256::from(x).into() 13 | } 14 | 15 | fn from_ark_fr(x: ArkFr) -> Fr { 16 | BigInteger256::from(x).into() 17 | } 18 | 19 | // Zeta is called QNR in the sage specification. 20 | pub const ZETA: Fq = Fq::from_montgomery_limbs([ 21 | 5947794125541564500, 22 | 11292571455564096885, 23 | 11814268415718120036, 24 | 155746270000486182, 25 | ]); 26 | 27 | // Constants used for square root computation // 28 | 29 | // N is the 2-adicity 30 | pub static N: u32 = 47; 31 | 32 | // M is `(p - 1) / 2^N` = 60001509534603559531609739528203892656505753216962260608619555 33 | pub static M: Lazy = Lazy::new(|| { 34 | let elem: ArkFq = 35 | ark_ff::MontFp!("60001509534603559531609739528203892656505753216962260608619555"); 36 | elem.into() 37 | }); 38 | 39 | // (M-1)/2 = 30000754767301779765804869764101946328252876608481130304309777 40 | pub static M_MINUS_ONE_DIV_TWO: Lazy = Lazy::new(|| { 41 | let elem: ArkFq = 42 | ark_ff::MontFp!("30000754767301779765804869764101946328252876608481130304309777"); 43 | elem.into() 44 | }); 45 | 46 | // ZETA**((1-M)/2) = 6762755396584113496485389421189479608933826763106393667349575256979972066439 47 | pub static ZETA_TO_ONE_MINUS_M_DIV_TWO: Lazy = Lazy::new(|| { 48 | from_ark_fq(ark_ff::MontFp!( 49 | "6762755396584113496485389421189479608933826763106393667349575256979972066439" 50 | )) 51 | }); 52 | 53 | // G = ZETA^M 54 | // = 4732611889701835744065511820927274956354524915951001256593514693060564426294 55 | pub static G: Lazy = Lazy::new(|| ZETA.pow(*M)); 56 | 57 | // Choice of W in the square root algorithm. 58 | pub static SQRT_W: u32 = 8; 59 | 60 | // Canonical basepoint projective coordinates 61 | pub const B_X: Fq = Fq::from_montgomery_limbs([ 62 | 5825153684096051627, 63 | 16988948339439369204, 64 | 186539475124256708, 65 | 1230075515893193738, 66 | ]); 67 | pub const B_Y: Fq = Fq::from_montgomery_limbs([ 68 | 9786171649960077610, 69 | 13527783345193426398, 70 | 10983305067350511165, 71 | 1251302644532346138, 72 | ]); 73 | pub const B_T: Fq = Fq::from_montgomery_limbs([ 74 | 7466800842436274004, 75 | 14314110021432015475, 76 | 14108125795146788134, 77 | 1305086759679105397, 78 | ]); 79 | pub const B_Z: Fq = Fq::ONE; 80 | 81 | // Canonical basepoint affine coordinates 82 | pub const GENERATOR_X: Fq = Fq::from_montgomery_limbs([ 83 | 5825153684096051627, 84 | 16988948339439369204, 85 | 186539475124256708, 86 | 1230075515893193738, 87 | ]); 88 | pub const GENERATOR_Y: Fq = Fq::from_montgomery_limbs([ 89 | 9786171649960077610, 90 | 13527783345193426398, 91 | 10983305067350511165, 92 | 1251302644532346138, 93 | ]); 94 | 95 | // Modulus of basefield 96 | pub static R: Lazy = Lazy::new(|| { 97 | from_ark_fr(ark_ff::MontFp!( 98 | "2111115437357092606062206234695386632838870926408408195193685246394721360383" 99 | )) 100 | }); 101 | -------------------------------------------------------------------------------- /src/ark_curve/edwards.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::{ 2 | twisted_edwards::{Affine, MontCurveConfig, Projective, TECurveConfig}, 3 | CurveConfig, 4 | }; 5 | 6 | use crate::ark_curve::constants::{GENERATOR_X, GENERATOR_Y}; 7 | use crate::{Fq, Fr}; 8 | 9 | #[derive(Clone, Default, PartialEq, Eq)] 10 | pub struct Decaf377EdwardsConfig; 11 | 12 | // These types should not be exported. They are similar to `EdwardsAffine` and 13 | // `EdwardsProjective` from the `ark_ed_on_bls12_377` crate, except using our own 14 | // `Decaf377Config` that has the cofactor set to 1. Consumers of this 15 | // library should use the `AffinePoint` and `Element` (projective) 16 | // types. 17 | pub type EdwardsAffine = Affine; 18 | pub type EdwardsProjective = Projective; 19 | 20 | impl CurveConfig for Decaf377EdwardsConfig { 21 | type BaseField = Fq; 22 | type ScalarField = Fr; 23 | 24 | const COFACTOR: &'static [u64] = &[1]; 25 | 26 | const COFACTOR_INV: Fr = Fr::ONE; 27 | } 28 | 29 | impl TECurveConfig for Decaf377EdwardsConfig { 30 | /// COEFF_A = -1 31 | const COEFF_A: Fq = Fq::from_montgomery_limbs([ 32 | 10157024534604021774, 33 | 16668528035959406606, 34 | 5322190058819395602, 35 | 387181115924875961, 36 | ]); 37 | 38 | /// COEFF_D = 3021 39 | const COEFF_D: Fq = Fq::from_montgomery_limbs([ 40 | 15008245758212136496, 41 | 17341409599856531410, 42 | 648869460136961410, 43 | 719771289660577536, 44 | ]); 45 | 46 | const GENERATOR: EdwardsAffine = EdwardsAffine::new_unchecked(GENERATOR_X, GENERATOR_Y); 47 | 48 | type MontCurveConfig = Decaf377EdwardsConfig; 49 | 50 | /// Multiplication by `a` is just negation. 51 | #[inline(always)] 52 | fn mul_by_a(elem: Self::BaseField) -> Self::BaseField { 53 | -elem 54 | } 55 | 56 | fn is_in_correct_subgroup_assuming_on_curve(_: &Affine) -> bool { 57 | true 58 | } 59 | } 60 | 61 | impl MontCurveConfig for Decaf377EdwardsConfig { 62 | const COEFF_A: Fq = Fq::from_montgomery_limbs([ 63 | 13800168384327121454, 64 | 6841573379969807446, 65 | 12529593083398462246, 66 | 853978956621483129, 67 | ]); 68 | 69 | const COEFF_B: Fq = Fq::from_montgomery_limbs([ 70 | 7239382437352637935, 71 | 14509846070439283655, 72 | 5083066350480839936, 73 | 1265663645916442191, 74 | ]); 75 | 76 | type TECurveConfig = Decaf377EdwardsConfig; 77 | } 78 | -------------------------------------------------------------------------------- /src/ark_curve/element.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::{AffineRepr, CurveGroup, Group, ScalarMul, VariableBaseMSM}; 2 | use ark_serialize::Valid; 3 | use ark_std::vec::Vec; 4 | 5 | use crate::{ 6 | ark_curve::{edwards::EdwardsAffine, Decaf377EdwardsConfig, EdwardsProjective}, 7 | Fq, Fr, 8 | }; 9 | 10 | pub mod affine; 11 | pub mod projective; 12 | 13 | pub use affine::AffinePoint; 14 | pub use projective::Element; 15 | 16 | impl Valid for Element { 17 | fn check(&self) -> Result<(), ark_serialize::SerializationError> { 18 | Ok(()) 19 | } 20 | } 21 | 22 | impl ScalarMul for Element { 23 | type MulBase = AffinePoint; 24 | 25 | const NEGATION_IS_CHEAP: bool = true; 26 | 27 | fn batch_convert_to_mul_base(bases: &[Self]) -> Vec { 28 | let bases_inner = bases.iter().map(|g| g.inner).collect::>(); 29 | let result = EdwardsProjective::batch_convert_to_mul_base(&bases_inner[..]); 30 | result 31 | .into_iter() 32 | .map(|g| AffinePoint { inner: g }) 33 | .collect::>() 34 | } 35 | } 36 | 37 | impl VariableBaseMSM for Element {} 38 | 39 | impl Group for Element { 40 | type ScalarField = Fr; 41 | 42 | fn double_in_place(&mut self) -> &mut Self { 43 | let inner = *self.inner.double_in_place(); 44 | *self = Element { inner }; 45 | self 46 | } 47 | 48 | fn generator() -> Self { 49 | Self::GENERATOR 50 | } 51 | 52 | fn mul_bigint(&self, other: impl AsRef<[u64]>) -> Self { 53 | let inner = self.inner.mul_bigint(other); 54 | Element { inner } 55 | } 56 | } 57 | 58 | impl CurveGroup for Element { 59 | // We implement `CurveGroup` as it is required by the `CurveVar` 60 | // trait used in the R1CS feature. The `ProjectiveCurve` trait requires 61 | // an affine representation of `Element` to be defined, and `AffineRepr` 62 | // to be implemented on that type. 63 | type Config = Decaf377EdwardsConfig; 64 | 65 | type BaseField = Fq; 66 | 67 | type Affine = AffinePoint; 68 | 69 | // This type is supposed to represent an element of the entire elliptic 70 | // curve group, not just the prime-order subgroup. Since this is decaf, 71 | // this is just an `Element` again. 72 | type FullGroup = AffinePoint; 73 | 74 | fn normalize_batch(v: &[Self]) -> Vec { 75 | let v_inner = v.iter().map(|g| g.inner).collect::>(); 76 | let result = EdwardsProjective::normalize_batch(&v_inner[..]); 77 | result 78 | .into_iter() 79 | .map(|g| AffinePoint { inner: g }) 80 | .collect::>() 81 | } 82 | 83 | fn into_affine(self) -> Self::Affine { 84 | self.into() 85 | } 86 | } 87 | 88 | impl Valid for AffinePoint { 89 | fn check(&self) -> Result<(), ark_serialize::SerializationError> { 90 | Ok(()) 91 | } 92 | } 93 | 94 | impl AffineRepr for AffinePoint { 95 | type Config = Decaf377EdwardsConfig; 96 | 97 | type ScalarField = Fr; 98 | 99 | type BaseField = Fq; 100 | 101 | type Group = Element; 102 | 103 | fn xy(&self) -> Option<(&Self::BaseField, &Self::BaseField)> { 104 | self.inner.xy() 105 | } 106 | 107 | fn zero() -> Self { 108 | AffinePoint { 109 | inner: EdwardsAffine::zero(), 110 | } 111 | } 112 | 113 | fn generator() -> Self { 114 | Element::GENERATOR.into() 115 | } 116 | 117 | fn from_random_bytes(bytes: &[u8]) -> Option { 118 | EdwardsAffine::from_random_bytes(bytes).map(|inner| AffinePoint { inner }) 119 | } 120 | 121 | fn mul_bigint(&self, other: impl AsRef<[u64]>) -> Self::Group { 122 | Element { 123 | inner: self.inner.mul_bigint(other), 124 | } 125 | } 126 | 127 | fn clear_cofactor(&self) -> Self { 128 | // This is decaf so we're just returning the same point. 129 | *self 130 | } 131 | 132 | fn mul_by_cofactor_to_group(&self) -> Self::Group { 133 | self.into() 134 | } 135 | } 136 | 137 | impl From for AffinePoint { 138 | fn from(point: Element) -> Self { 139 | Self { 140 | inner: point.inner.into(), 141 | } 142 | } 143 | } 144 | 145 | impl From for Element { 146 | fn from(point: AffinePoint) -> Self { 147 | Self { 148 | inner: point.inner.into(), 149 | } 150 | } 151 | } 152 | 153 | impl From<&Element> for AffinePoint { 154 | fn from(point: &Element) -> Self { 155 | Self { 156 | inner: point.inner.into(), 157 | } 158 | } 159 | } 160 | 161 | impl From<&AffinePoint> for Element { 162 | fn from(point: &AffinePoint) -> Self { 163 | Self { 164 | inner: point.inner.into(), 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/ark_curve/element/affine.rs: -------------------------------------------------------------------------------- 1 | use core::hash::Hash; 2 | 3 | use crate::ark_curve::element::EdwardsAffine; 4 | use ark_std::fmt::{Display, Formatter, Result as FmtResult}; 5 | use ark_std::Zero; 6 | 7 | use zeroize::Zeroize; 8 | 9 | use crate::Element; 10 | 11 | #[derive(Copy, Clone)] 12 | pub struct AffinePoint { 13 | pub(crate) inner: EdwardsAffine, 14 | } 15 | 16 | impl Hash for AffinePoint { 17 | fn hash(&self, state: &mut H) { 18 | self.inner.hash(state); 19 | } 20 | } 21 | 22 | impl Default for AffinePoint { 23 | fn default() -> Self { 24 | Element::default().into() 25 | } 26 | } 27 | 28 | impl core::iter::Sum for Element { 29 | fn sum>(iter: I) -> Self { 30 | iter.fold(Self::zero(), core::ops::Add::add) 31 | } 32 | } 33 | 34 | impl<'a> core::iter::Sum<&'a AffinePoint> for Element { 35 | fn sum>(iter: I) -> Self { 36 | iter.fold(Self::zero(), core::ops::Add::add) 37 | } 38 | } 39 | 40 | impl PartialEq for AffinePoint { 41 | fn eq(&self, other: &AffinePoint) -> bool { 42 | // Section 4.5 of Decaf paper 43 | self.inner.x * other.inner.y == self.inner.y * other.inner.x 44 | } 45 | } 46 | 47 | impl Eq for AffinePoint {} 48 | 49 | impl core::fmt::Debug for AffinePoint { 50 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 51 | let element: Element = self.into(); 52 | f.write_fmt(format_args!( 53 | "decaf377::AffinePoint({})", 54 | hex::encode(&element.vartime_compress().0[..]) 55 | )) 56 | } 57 | } 58 | 59 | impl Display for AffinePoint { 60 | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 61 | let element: Element = self.into(); 62 | write!( 63 | f, 64 | "decaf377::AffinePoint({})", 65 | hex::encode(&element.vartime_compress().0[..]) 66 | ) 67 | } 68 | } 69 | 70 | impl Zeroize for AffinePoint { 71 | fn zeroize(&mut self) { 72 | self.inner.zeroize() 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/ark_curve/element/projective.rs: -------------------------------------------------------------------------------- 1 | use core::borrow::Borrow; 2 | use core::hash::Hash; 3 | 4 | use ark_ff::Zero; 5 | use ark_std::fmt::{Display, Formatter, Result as FmtResult}; 6 | 7 | use zeroize::Zeroize; 8 | 9 | use crate::{ark_curve::EdwardsProjective, Fq, Fr}; 10 | 11 | use super::super::constants::{B_T, B_X, B_Y, B_Z}; 12 | 13 | #[derive(Copy, Clone)] 14 | pub struct Element { 15 | pub(crate) inner: EdwardsProjective, 16 | } 17 | 18 | impl Element { 19 | /// Return the conventional generator for `decaf377`. 20 | pub const GENERATOR: Self = Self { 21 | inner: EdwardsProjective::new_unchecked(B_X, B_Y, B_T, B_Z), 22 | }; 23 | 24 | pub const IDENTITY: Self = Self { 25 | inner: EdwardsProjective::new_unchecked(Fq::ZERO, Fq::ONE, Fq::ZERO, Fq::ONE), 26 | }; 27 | } 28 | 29 | impl Hash for Element { 30 | fn hash(&self, state: &mut H) { 31 | self.inner.hash(state); 32 | } 33 | } 34 | 35 | impl core::fmt::Debug for Element { 36 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 37 | // This prints the hex of the encoding of self, rather than the 38 | // coordinates, because that's what's most useful to downstream 39 | // consumers of the library. 40 | f.write_fmt(format_args!( 41 | "decaf377::Element({})", 42 | hex::encode(&self.vartime_compress().0[..]) 43 | )) 44 | } 45 | } 46 | 47 | impl Display for Element { 48 | fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult { 49 | write!( 50 | f, 51 | "decaf377::Element({})", 52 | hex::encode(&self.vartime_compress().0[..]) 53 | ) 54 | } 55 | } 56 | 57 | impl Default for Element { 58 | fn default() -> Self { 59 | Element { 60 | inner: EdwardsProjective::zero(), 61 | } 62 | } 63 | } 64 | 65 | impl PartialEq for Element { 66 | fn eq(&self, other: &Element) -> bool { 67 | // Section 4.5 of Decaf paper 68 | self.inner.x * other.inner.y == self.inner.y * other.inner.x 69 | } 70 | } 71 | 72 | impl Eq for Element {} 73 | 74 | impl Zeroize for Element { 75 | fn zeroize(&mut self) { 76 | self.inner.zeroize() 77 | } 78 | } 79 | 80 | impl Element { 81 | /// Convenience method to make identity checks more readable. 82 | pub fn is_identity(&self) -> bool { 83 | // Section 4.5 of Decaf paper states for cofactor 4 curves we can 84 | // just check X = 0 to check equality with identity 85 | self.inner.x == Fq::zero() 86 | } 87 | 88 | /// Given an iterator of public scalars and an iterator of public points, 89 | /// compute 90 | /// $$ 91 | /// Q = \[c\_1\] P\_1 + \cdots + \[c\_n\] P\_n, 92 | /// $$ 93 | /// using variable-time operations. 94 | /// 95 | /// It is an error to call this function with two iterators of different 96 | /// lengths -- it would require `ExactSizeIterator`, but 97 | /// `ExactSizeIterator`s are not closed under chaining, and disallowing 98 | /// iterator chaining would destroy the utility of the function. 99 | pub fn vartime_multiscalar_mul(scalars: I, points: J) -> Element 100 | where 101 | I: IntoIterator, 102 | I::Item: Borrow, 103 | J: IntoIterator, 104 | J::Item: Borrow, 105 | { 106 | // XXX this is a stub implementation, try to use a real MSM later 107 | let scalars = scalars.into_iter(); 108 | let points = points.into_iter(); 109 | 110 | // XXX panic on length mismatches ? or error? 111 | 112 | scalars 113 | .zip(points) 114 | .fold(Element::default(), |acc, (scalar, point)| { 115 | acc + (scalar.borrow() * point.borrow()) 116 | }) 117 | } 118 | } 119 | 120 | impl Zero for Element { 121 | fn zero() -> Self { 122 | Self::default() 123 | } 124 | 125 | fn is_zero(&self) -> bool { 126 | self.inner.is_zero() 127 | } 128 | } 129 | 130 | impl core::iter::Sum for Element { 131 | fn sum>(iter: I) -> Self { 132 | iter.fold(Self::zero(), core::ops::Add::add) 133 | } 134 | } 135 | 136 | impl<'a> core::iter::Sum<&'a Element> for Element { 137 | fn sum>(iter: I) -> Self { 138 | iter.fold(Self::zero(), core::ops::Add::add) 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/ark_curve/elligator.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | use ark_ec::twisted_edwards::TECurveConfig; 3 | 4 | use crate::ark_curve::edwards::{Decaf377EdwardsConfig, EdwardsProjective}; 5 | 6 | use crate::{ 7 | ark_curve::constants::{ONE, TWO, ZETA}, 8 | ark_curve::on_curve::OnCurve, 9 | sign::Sign, 10 | Element, Fq, 11 | }; 12 | 13 | impl Element { 14 | /// Elligator 2 map to decaf377 point 15 | fn elligator_map(r_0: &Fq) -> Element { 16 | // Ref: `Decaf_1_1_Point.elligator` (optimized) in `ristretto.sage` 17 | let A = Decaf377EdwardsConfig::COEFF_A; 18 | let D = Decaf377EdwardsConfig::COEFF_D; 19 | 20 | let r = ZETA * r_0.square(); 21 | 22 | let den = (D * r - (D - A)) * ((D - A) * r - D); 23 | let num = (r + *ONE) * (A - *TWO * D); 24 | 25 | let x = num * den; 26 | let (iss, mut isri) = Fq::sqrt_ratio_zeta(&ONE, &x); 27 | 28 | let sgn; 29 | let twiddle; 30 | if iss { 31 | sgn = *ONE; 32 | twiddle = *ONE; 33 | } else { 34 | sgn = -(*ONE); 35 | twiddle = *r_0; 36 | } 37 | 38 | isri *= twiddle; 39 | 40 | let mut s = isri * num; 41 | let t = -(sgn) * isri * s * (r - *ONE) * (A - *TWO * D).square() - *ONE; 42 | 43 | if s.is_negative() == iss { 44 | s = -s 45 | } 46 | 47 | // Convert point to extended projective (X : Y : Z : T) 48 | let E = *TWO * s; 49 | let F = *ONE + Decaf377EdwardsConfig::COEFF_A * s.square(); 50 | let G = *ONE - Decaf377EdwardsConfig::COEFF_A * s.square(); 51 | let H = t; 52 | let result = Element { 53 | inner: EdwardsProjective::new(E * H, F * G, E * G, F * H), 54 | }; 55 | 56 | debug_assert!( 57 | result.inner.is_on_curve(), 58 | "resulting point must be on the curve", 59 | ); 60 | 61 | result 62 | } 63 | 64 | /// Maps two field elements to a uniformly distributed decaf377 `Element`. 65 | /// 66 | /// The two field elements provided as inputs should be independently chosen. 67 | pub fn hash_to_curve(r_1: &Fq, r_2: &Fq) -> Element { 68 | let R_1 = Element::elligator_map(r_1); 69 | let R_2 = Element::elligator_map(r_2); 70 | &R_1 + &R_2 71 | } 72 | 73 | /// Maps a field element to a decaf377 `Element` suitable for CDH challenges. 74 | pub fn encode_to_curve(r: &Fq) -> Element { 75 | Element::elligator_map(r) 76 | } 77 | } 78 | 79 | #[cfg(test)] 80 | mod tests { 81 | use crate::ark_curve::edwards::EdwardsAffine; 82 | 83 | use super::*; 84 | 85 | #[test] 86 | fn test_elligator() { 87 | // These are the test cases from testElligatorDeterministic in ristretto.sage 88 | let inputs = [ 89 | [ 90 | 221, 101, 215, 58, 170, 229, 36, 124, 172, 234, 94, 214, 186, 163, 242, 30, 65, 91 | 123, 76, 74, 56, 60, 24, 213, 240, 137, 49, 189, 138, 39, 90, 6, 92 | ], 93 | [ 94 | 23, 203, 214, 51, 26, 149, 7, 160, 228, 239, 208, 147, 124, 109, 75, 72, 64, 16, 95 | 64, 215, 53, 185, 249, 168, 188, 49, 22, 194, 118, 7, 242, 16, 96 | ], 97 | [ 98 | 177, 123, 90, 180, 115, 7, 108, 183, 161, 167, 24, 15, 248, 218, 206, 227, 76, 137, 99 | 162, 187, 148, 174, 66, 44, 205, 1, 211, 91, 140, 50, 144, 1, 100 | ], 101 | [ 102 | 204, 225, 121, 228, 145, 30, 86, 208, 132, 242, 203, 9, 153, 90, 195, 150, 215, 49, 103 | 166, 70, 78, 68, 47, 98, 30, 130, 115, 139, 168, 242, 238, 8, 104 | ], 105 | [ 106 | 59, 150, 40, 159, 229, 96, 201, 47, 170, 163, 9, 208, 205, 201, 112, 241, 179, 82, 107 | 198, 79, 207, 160, 184, 245, 63, 189, 101, 115, 217, 228, 74, 13, 108 | ], 109 | [ 110 | 74, 159, 227, 190, 73, 213, 131, 200, 50, 102, 249, 230, 48, 103, 85, 168, 239, 111 | 149, 7, 164, 12, 42, 217, 177, 189, 97, 214, 98, 102, 73, 10, 16, 112 | ], 113 | [ 114 | 183, 227, 227, 192, 119, 10, 155, 143, 64, 60, 249, 165, 240, 39, 31, 197, 159, 115 | 121, 64, 82, 10, 1, 34, 35, 121, 34, 146, 69, 226, 196, 156, 14, 116 | ], 117 | [ 118 | 61, 21, 56, 224, 11, 181, 71, 186, 238, 126, 234, 240, 14, 168, 75, 73, 251, 111, 119 | 175, 85, 108, 9, 77, 2, 88, 249, 24, 235, 53, 96, 51, 15, 120 | ], 121 | ]; 122 | 123 | let expected_xy_coordinates = [ 124 | [ 125 | ark_ff::MontFp!( 126 | "1267955849280145133999011095767946180059440909377398529682813961428156596086" 127 | ), 128 | ark_ff::MontFp!( 129 | "5356565093348124788258444273601808083900527100008973995409157974880178412098" 130 | ), 131 | ], 132 | [ 133 | ark_ff::MontFp!( 134 | "1502379126429822955521756759528876454108853047288874182661923263559139887582" 135 | ), 136 | ark_ff::MontFp!( 137 | "7074060208122316523843780248565740332109149189893811936352820920606931717751" 138 | ), 139 | ], 140 | [ 141 | ark_ff::MontFp!( 142 | "2943006201157313879823661217587757631000260143892726691725524748591717287835" 143 | ), 144 | ark_ff::MontFp!( 145 | "4988568968545687084099497807398918406354768651099165603393269329811556860241" 146 | ), 147 | ], 148 | [ 149 | ark_ff::MontFp!( 150 | "2893226299356126359042735859950249532894422276065676168505232431940642875576" 151 | ), 152 | ark_ff::MontFp!( 153 | "5540423804567408742733533031617546054084724133604190833318816134173899774745" 154 | ), 155 | ], 156 | [ 157 | ark_ff::MontFp!( 158 | "2950911977149336430054248283274523588551527495862004038190631992225597951816" 159 | ), 160 | ark_ff::MontFp!( 161 | "4487595759841081228081250163499667279979722963517149877172642608282938805393" 162 | ), 163 | ], 164 | [ 165 | ark_ff::MontFp!( 166 | "3318574188155535806336376903248065799756521242795466350457330678746659358665" 167 | ), 168 | ark_ff::MontFp!( 169 | "7706453242502782485686954136003233626318476373744684895503194201695334921001" 170 | ), 171 | ], 172 | [ 173 | ark_ff::MontFp!( 174 | "3753408652523927772367064460787503971543824818235418436841486337042861871179" 175 | ), 176 | ark_ff::MontFp!( 177 | "2820605049615187268236268737743168629279853653807906481532750947771625104256" 178 | ), 179 | ], 180 | [ 181 | ark_ff::MontFp!( 182 | "7803875556376973796629423752730968724982795310878526731231718944925551226171" 183 | ), 184 | ark_ff::MontFp!( 185 | "7033839813997913565841973681083930410776455889380940679209912201081069572111" 186 | ), 187 | ], 188 | ]; 189 | 190 | use ark_serialize::CanonicalDeserialize; 191 | 192 | for (ind, input) in inputs.iter().enumerate() { 193 | let input_element = 194 | Fq::deserialize_compressed(&input[..]).expect("encoding of test vector is valid"); 195 | 196 | let expected: Element = Element { 197 | inner: EdwardsAffine::new( 198 | crate::ark_curve::constants::from_ark_fq(expected_xy_coordinates[ind][0]), 199 | crate::ark_curve::constants::from_ark_fq(expected_xy_coordinates[ind][1]), 200 | ) 201 | .into(), 202 | }; 203 | 204 | let actual = Element::elligator_map(&input_element); 205 | 206 | assert_eq!(actual, expected); 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/ark_curve/encoding.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use core::convert::{TryFrom, TryInto}; 4 | 5 | use ark_ec::twisted_edwards::TECurveConfig; 6 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, Write}; 7 | 8 | use crate::ark_curve::{ 9 | constants::TWO, edwards::Decaf377EdwardsConfig, on_curve::OnCurve, EdwardsProjective, Element, 10 | }; 11 | use crate::sign::Sign; 12 | use crate::{EncodingError, Fq}; 13 | 14 | #[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq)] 15 | pub struct Encoding(pub [u8; 32]); 16 | 17 | impl core::fmt::Debug for Encoding { 18 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 19 | f.write_fmt(format_args!( 20 | "decaf377::Encoding({})", 21 | hex::encode(&self.0[..]) 22 | )) 23 | } 24 | } 25 | 26 | impl Encoding { 27 | #[deprecated(note = "please use `vartime_decompress` instead")] 28 | pub fn decompress(&self) -> Result { 29 | self.vartime_decompress() 30 | } 31 | 32 | pub fn vartime_decompress(&self) -> Result { 33 | // Top three bits of last byte should be zero 34 | if self.0[31] >> 5 != 0u8 { 35 | return Err(EncodingError::InvalidEncoding); 36 | } 37 | 38 | // This isn't a constant, only because traits don't have const methods 39 | // yet and multiplication is only implemented as part of the Mul trait. 40 | let D4: Fq = Decaf377EdwardsConfig::COEFF_D * Fq::from(4u32); 41 | 42 | // 1/2. Reject unless s is canonically encoded and nonnegative. 43 | let s = 44 | Fq::deserialize_compressed(&self.0[..]).map_err(|_| EncodingError::InvalidEncoding)?; 45 | if s.is_negative() { 46 | return Err(EncodingError::InvalidEncoding); 47 | } 48 | 49 | // 3. u_1 <- 1 - s^2 50 | let ss = s.square(); 51 | let u_1 = Fq::ONE - ss; 52 | 53 | // 4. u_2 <- u_1^2 - 4d s^2 54 | let u_2 = u_1.square() - D4 * ss; 55 | 56 | // 5. sqrt 57 | let (was_square, mut v) = Fq::sqrt_ratio_zeta(&Fq::ONE, &(u_2 * u_1.square())); 58 | if !was_square { 59 | return Err(EncodingError::InvalidEncoding); 60 | } 61 | 62 | // 6. sign check 63 | let two_s_u_1 = *TWO * s * u_1; 64 | let check = two_s_u_1 * v; 65 | if check.is_negative() { 66 | v = -v; 67 | } 68 | 69 | // 7. coordinates 70 | let x = two_s_u_1 * v.square() * u_2; 71 | let y = (Fq::ONE + ss) * v * u_1; 72 | let z = Fq::ONE; 73 | let t = x * y; 74 | 75 | debug_assert!( 76 | EdwardsProjective::new(x, y, t, z).is_on_curve(), 77 | "resulting point must be on the curve", 78 | ); 79 | 80 | Ok(Element { 81 | inner: EdwardsProjective::new(x, y, t, z), 82 | }) 83 | } 84 | } 85 | 86 | impl Element { 87 | pub fn negate(&self) -> Element { 88 | Element { inner: -self.inner } 89 | } 90 | 91 | pub fn vartime_compress_to_field(&self) -> Fq { 92 | // This isn't a constant, only because traits don't have const methods 93 | // yet and subtraction is only implemented as part of the Sub trait. 94 | let A_MINUS_D = Decaf377EdwardsConfig::COEFF_A - Decaf377EdwardsConfig::COEFF_D; 95 | let p = &self.inner; 96 | 97 | // 1. 98 | let u_1 = (p.x + p.t) * (p.x - p.t); 99 | 100 | // 2. division by 0 occurs on the identity point, but since 101 | // sqrt_ratio_zeta outputs v=0 it computes the right encoding anyway 102 | let (_always_square, v) = Fq::sqrt_ratio_zeta(&Fq::ONE, &(u_1 * A_MINUS_D * p.x.square())); 103 | 104 | // 3. 105 | let u_2 = (v * u_1).abs(); 106 | 107 | // 4. 108 | let u_3 = u_2 * p.z - p.t; 109 | 110 | // 5. 111 | let s = (A_MINUS_D * v * u_3 * p.x).abs(); 112 | 113 | s 114 | } 115 | 116 | pub fn vartime_compress(&self) -> Encoding { 117 | let s = self.vartime_compress_to_field(); 118 | 119 | // Encode. 120 | let mut bytes = [0u8; 32]; 121 | debug_assert_eq!(s.serialized_size(ark_serialize::Compress::Yes), 32); 122 | s.serialize_compressed(&mut bytes[..]) 123 | .expect("serialization into array should be infallible"); 124 | // Set top three bits of last byte to zero 125 | bytes[31] &= 0b00011111; 126 | 127 | Encoding(bytes) 128 | } 129 | } 130 | 131 | impl From<&Element> for Encoding { 132 | fn from(point: &Element) -> Self { 133 | point.vartime_compress() 134 | } 135 | } 136 | 137 | impl From for Encoding { 138 | fn from(point: Element) -> Self { 139 | point.vartime_compress() 140 | } 141 | } 142 | 143 | impl CanonicalSerialize for Encoding { 144 | fn serialized_size(&self, compress: ark_serialize::Compress) -> usize { 145 | match compress { 146 | ark_serialize::Compress::Yes => 32, 147 | ark_serialize::Compress::No => unimplemented!(), 148 | } 149 | } 150 | 151 | fn serialize_with_mode( 152 | &self, 153 | mut writer: W, 154 | _mode: ark_serialize::Compress, 155 | ) -> Result<(), ark_serialize::SerializationError> { 156 | writer.write_all(&self.0[..])?; 157 | Ok(()) 158 | } 159 | } 160 | 161 | impl CanonicalSerialize for Element { 162 | fn serialized_size(&self, compress: ark_serialize::Compress) -> usize { 163 | match compress { 164 | ark_serialize::Compress::Yes => 32, 165 | ark_serialize::Compress::No => unimplemented!(), 166 | } 167 | } 168 | 169 | fn serialize_with_mode( 170 | &self, 171 | writer: W, 172 | mode: ark_serialize::Compress, 173 | ) -> Result<(), ark_serialize::SerializationError> { 174 | self.vartime_compress().serialize_with_mode(writer, mode) 175 | } 176 | } 177 | 178 | impl TryFrom<&[u8]> for Encoding { 179 | type Error = EncodingError; 180 | 181 | fn try_from(bytes: &[u8]) -> Result { 182 | if bytes.len() == 32 { 183 | let mut arr = [0u8; 32]; 184 | arr.copy_from_slice(&bytes[0..32]); 185 | Ok(Encoding(arr)) 186 | } else { 187 | Err(EncodingError::InvalidSliceLength) 188 | } 189 | } 190 | } 191 | 192 | impl From<[u8; 32]> for Encoding { 193 | fn from(bytes: [u8; 32]) -> Encoding { 194 | Encoding(bytes) 195 | } 196 | } 197 | 198 | impl From for [u8; 32] { 199 | fn from(enc: Encoding) -> [u8; 32] { 200 | enc.0 201 | } 202 | } 203 | 204 | impl TryFrom<&Encoding> for Element { 205 | type Error = EncodingError; 206 | fn try_from(bytes: &Encoding) -> Result { 207 | bytes.vartime_decompress() 208 | } 209 | } 210 | 211 | impl TryFrom for Element { 212 | type Error = EncodingError; 213 | fn try_from(bytes: Encoding) -> Result { 214 | bytes.vartime_decompress() 215 | } 216 | } 217 | 218 | impl TryFrom<&[u8]> for Element { 219 | type Error = EncodingError; 220 | 221 | fn try_from(bytes: &[u8]) -> Result { 222 | let b: [u8; 32] = bytes 223 | .try_into() 224 | .map_err(|_| EncodingError::InvalidSliceLength)?; 225 | 226 | Encoding(b).try_into() 227 | } 228 | } 229 | 230 | impl TryFrom<[u8; 32]> for Element { 231 | type Error = EncodingError; 232 | 233 | fn try_from(bytes: [u8; 32]) -> Result { 234 | let encoding = Encoding(bytes); 235 | encoding.try_into() 236 | } 237 | } 238 | 239 | impl From for [u8; 32] { 240 | fn from(enc: Element) -> [u8; 32] { 241 | enc.vartime_compress().0 242 | } 243 | } 244 | 245 | impl ark_serialize::Valid for Encoding { 246 | fn check(&self) -> Result<(), ark_serialize::SerializationError> { 247 | // At this stage, validity just means the point has 32 bytes 248 | // which is encoded in the type information. 249 | Ok(()) 250 | } 251 | } 252 | 253 | impl CanonicalDeserialize for Encoding { 254 | fn deserialize_with_mode( 255 | mut reader: R, 256 | compress: ark_serialize::Compress, 257 | validate: ark_serialize::Validate, 258 | ) -> Result { 259 | match compress { 260 | ark_serialize::Compress::Yes => (), 261 | ark_serialize::Compress::No => unimplemented!(), 262 | } 263 | match validate { 264 | ark_serialize::Validate::Yes => (), 265 | ark_serialize::Validate::No => unimplemented!(), 266 | } 267 | let mut bytes = [0u8; 32]; 268 | reader.read_exact(&mut bytes[..])?; 269 | Ok(Self(bytes)) 270 | } 271 | } 272 | 273 | impl CanonicalDeserialize for Element { 274 | fn deserialize_with_mode( 275 | reader: R, 276 | compress: ark_serialize::Compress, 277 | validate: ark_serialize::Validate, 278 | ) -> Result { 279 | match compress { 280 | ark_serialize::Compress::Yes => (), 281 | ark_serialize::Compress::No => unimplemented!(), 282 | } 283 | match validate { 284 | ark_serialize::Validate::Yes => (), 285 | ark_serialize::Validate::No => unimplemented!(), 286 | } 287 | let bytes = Encoding::deserialize_compressed(reader)?; 288 | bytes 289 | .try_into() 290 | .map_err(|_| ark_serialize::SerializationError::InvalidData) 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/ark_curve/invsqrt.rs: -------------------------------------------------------------------------------- 1 | use core::convert::TryInto; 2 | use hashbrown::HashMap; 3 | 4 | use crate::Fq; 5 | use ark_ff::{BigInteger256, BigInteger64, Field, Zero}; 6 | use ark_std::boxed::Box; 7 | use ark_std::vec::Vec; 8 | use once_cell::sync::Lazy; 9 | 10 | use crate::ark_curve::constants::{ 11 | G, M_MINUS_ONE_DIV_TWO, N, ONE, SQRT_W, ZETA_TO_ONE_MINUS_M_DIV_TWO, 12 | }; 13 | 14 | struct SquareRootTables { 15 | pub s_lookup: HashMap, 16 | pub nonsquare_lookup: [Fq; 2], 17 | pub g0: Box<[Fq; 256]>, 18 | pub g8: Box<[Fq; 256]>, 19 | pub g16: Box<[Fq; 256]>, 20 | pub g24: Box<[Fq; 256]>, 21 | pub g32: Box<[Fq; 256]>, 22 | pub g40: Box<[Fq; 256]>, 23 | } 24 | 25 | impl SquareRootTables { 26 | fn new() -> Self { 27 | let mut s_lookup = HashMap::new(); 28 | for nu in 0..256 { 29 | // These entries should be g**(-1 * nu * 2**(n-w)) so: 30 | // 1. We compute g**(nu * 2**(n-w)) 31 | // 2. Then take the inverse of the quantity from step 1 32 | let exp: BigInteger256 = (nu * 2u64.pow(N - SQRT_W)).into(); 33 | let g_inv = G.pow(exp); 34 | s_lookup.insert( 35 | g_inv.inverse().expect("inverse exists for these elements"), 36 | nu, 37 | ); 38 | } 39 | 40 | let powers_of_two = [0, 8, 16, 24, 32, 40]; 41 | let mut gtab = Vec::new(); 42 | for power_of_two in powers_of_two { 43 | let mut gtab_i = Vec::::new(); 44 | for nu in 0..256 { 45 | let exp: BigInteger256 = (nu * 2u64.pow(power_of_two)).into(); 46 | gtab_i.push(G.pow(exp)) 47 | } 48 | gtab.push(gtab_i); 49 | } 50 | 51 | let nonsquare_lookup = [*ONE, *ZETA_TO_ONE_MINUS_M_DIV_TWO]; 52 | 53 | Self { 54 | s_lookup, 55 | nonsquare_lookup, 56 | g40: gtab.pop().unwrap().into_boxed_slice().try_into().unwrap(), 57 | g32: gtab.pop().unwrap().into_boxed_slice().try_into().unwrap(), 58 | g24: gtab.pop().unwrap().into_boxed_slice().try_into().unwrap(), 59 | g16: gtab.pop().unwrap().into_boxed_slice().try_into().unwrap(), 60 | g8: gtab.pop().unwrap().into_boxed_slice().try_into().unwrap(), 61 | g0: gtab.pop().unwrap().into_boxed_slice().try_into().unwrap(), 62 | } 63 | } 64 | } 65 | 66 | static SQRT_LOOKUP_TABLES: Lazy = Lazy::new(|| SquareRootTables::new()); 67 | 68 | impl Fq { 69 | /// Computes the square root of a ratio of field elements, returning: 70 | /// 71 | /// - `(true, sqrt(num/den))` if `num` and `den` are both nonzero and `num/den` is square; 72 | /// - `(true, 0)` if `num` is zero; 73 | /// - `(false, 0)` if `den` is zero; 74 | /// - `(false, sqrt(zeta*num/den))` if `num` and `den` are both nonzero and `num/den` is nonsquare; 75 | pub fn sqrt_ratio_zeta(num: &Self, den: &Self) -> (bool, Self) { 76 | // This square root method is based on: 77 | // * [Sarkar2020](https://eprint.iacr.org/2020/1407) 78 | // * [Zcash Pasta](https://github.com/zcash/pasta_curves) 79 | // 80 | // See the Penumbra protocol specification for details. 81 | if num.is_zero() { 82 | return (true, *num); 83 | } 84 | if den.is_zero() { 85 | return (false, *den); 86 | } 87 | 88 | let s_exp: BigInteger256 = (2u64.pow(N) - 1).into(); 89 | let s = den.pow(s_exp); 90 | let t = s.square() * den; 91 | let w = (*num * t).pow(*M_MINUS_ONE_DIV_TWO) * s; 92 | 93 | let v = w * den; 94 | let uv = w * num; 95 | 96 | // x = u * v^2 = x5 97 | let x5 = uv * v; 98 | let pow2_8: BigInteger64 = 2u64.pow(8).into(); 99 | 100 | // x4 = x5^{2^8} 101 | let x4 = x5.pow(pow2_8); 102 | // x3 = x4^{2^8} 103 | let x3 = x4.pow(pow2_8); 104 | // x2 = x3^{2^8} 105 | let x2 = x3.pow(pow2_8); 106 | // x1 = x2^{2^8} 107 | let x1 = x2.pow(pow2_8); 108 | let pow2_7: BigInteger64 = 2u64.pow(7).into(); 109 | // x0 = x1^{2^7} 110 | let x0 = x1.pow(pow2_7); 111 | 112 | // i = 0 113 | let q0_prime = SQRT_LOOKUP_TABLES.s_lookup[&x0]; 114 | let mut t = q0_prime; 115 | 116 | // i = 1 117 | let alpha_1 = x1 * SQRT_LOOKUP_TABLES.g32[(t & 0xFF) as usize]; 118 | let q1_prime = SQRT_LOOKUP_TABLES.s_lookup[&alpha_1]; 119 | t += q1_prime << 7; 120 | 121 | // i = 2 122 | let alpha_2 = x2 123 | * SQRT_LOOKUP_TABLES.g24[(t & 0xFF) as usize] 124 | * SQRT_LOOKUP_TABLES.g32[((t >> 8) & 0xFF) as usize]; 125 | let q2 = SQRT_LOOKUP_TABLES.s_lookup[&alpha_2]; 126 | t += q2 << 15; 127 | 128 | // i = 3 129 | let alpha_3 = x3 130 | * SQRT_LOOKUP_TABLES.g16[(t & 0xFF) as usize] 131 | * SQRT_LOOKUP_TABLES.g24[((t >> 8) & 0xFF) as usize] 132 | * SQRT_LOOKUP_TABLES.g32[((t >> 16) & 0xFF) as usize]; 133 | let q3 = SQRT_LOOKUP_TABLES.s_lookup[&alpha_3]; 134 | t += q3 << 23; 135 | 136 | // i = 4 137 | let alpha_4 = x4 138 | * SQRT_LOOKUP_TABLES.g8[(t & 0xFF) as usize] 139 | * SQRT_LOOKUP_TABLES.g16[((t >> 8) & 0xFF) as usize] 140 | * SQRT_LOOKUP_TABLES.g24[((t >> 16) & 0xFF) as usize] 141 | * SQRT_LOOKUP_TABLES.g32[((t >> 24) & 0xFF) as usize]; 142 | let q4 = SQRT_LOOKUP_TABLES.s_lookup[&alpha_4]; 143 | t += q4 << 31; 144 | 145 | // i = 5 146 | let alpha_5 = x5 147 | * SQRT_LOOKUP_TABLES.g0[(t & 0xFF) as usize] 148 | * SQRT_LOOKUP_TABLES.g8[((t >> 8) & 0xFF) as usize] 149 | * SQRT_LOOKUP_TABLES.g16[((t >> 16) & 0xFF) as usize] 150 | * SQRT_LOOKUP_TABLES.g24[((t >> 24) & 0xFF) as usize] 151 | * SQRT_LOOKUP_TABLES.g32[((t >> 32) & 0xFF) as usize]; 152 | let q5 = SQRT_LOOKUP_TABLES.s_lookup[&alpha_5]; 153 | t += q5 << 39; 154 | 155 | t = (t + 1) >> 1; 156 | let res: Fq = uv 157 | * SQRT_LOOKUP_TABLES.nonsquare_lookup[(q0_prime & 0b1) as usize] 158 | * SQRT_LOOKUP_TABLES.g0[(t & 0xFF) as usize] 159 | * SQRT_LOOKUP_TABLES.g8[((t >> 8) & 0xFF) as usize] 160 | * SQRT_LOOKUP_TABLES.g16[((t >> 16) & 0xFF) as usize] 161 | * SQRT_LOOKUP_TABLES.g24[((t >> 24) & 0xFF) as usize] 162 | * SQRT_LOOKUP_TABLES.g32[((t >> 32) & 0xFF) as usize] 163 | * SQRT_LOOKUP_TABLES.g40[((t >> 40) & 0xFF) as usize]; 164 | 165 | ((q0_prime & 0b1) == 0, res) 166 | } 167 | } 168 | 169 | #[cfg(test)] 170 | mod tests { 171 | use super::*; 172 | use crate::ark_curve::constants::ZETA; 173 | 174 | use proptest::prelude::*; 175 | 176 | fn fq_strategy() -> BoxedStrategy { 177 | any::<[u8; 32]>() 178 | .prop_map(|bytes| Fq::from_le_bytes_mod_order(&bytes[..])) 179 | .boxed() 180 | } 181 | 182 | proptest! { 183 | #![proptest_config(ProptestConfig::with_cases(10000))] 184 | #[test] 185 | fn sqrt_ratio_zeta(u in fq_strategy(), v in fq_strategy()) { 186 | if u.is_zero() { 187 | assert_eq!(Fq::sqrt_ratio_zeta(&u, &v), (true, u)); 188 | } else if v.is_zero() { 189 | assert_eq!(Fq::sqrt_ratio_zeta(&u, &v), (false, v)); 190 | } else { 191 | let (was_square, sqrt_zeta_uv) = Fq::sqrt_ratio_zeta(&u, &v); 192 | let zeta_uv = sqrt_zeta_uv * sqrt_zeta_uv; 193 | if was_square { 194 | // check zeta_uv = u/v 195 | assert_eq!(u, v * zeta_uv); 196 | } else { 197 | // check zeta_uv = zeta * u / v 198 | assert_eq!(ZETA * u, v * zeta_uv); 199 | } 200 | } 201 | } 202 | } 203 | 204 | #[test] 205 | fn sqrt_ratio_edge_cases() { 206 | // u = 0 207 | assert_eq!(Fq::sqrt_ratio_zeta(&Fq::zero(), &ONE), (true, Fq::zero())); 208 | 209 | // v = 0 210 | assert_eq!(Fq::sqrt_ratio_zeta(&ONE, &Fq::zero()), (false, Fq::zero())); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/ark_curve/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod bls12_377; 2 | mod constants; 3 | mod edwards; 4 | 5 | mod element; 6 | mod elligator; 7 | mod encoding; 8 | mod invsqrt; 9 | mod ops; 10 | pub mod rand; 11 | pub mod serialize; 12 | 13 | pub use constants::ZETA; 14 | pub(crate) use edwards::{Decaf377EdwardsConfig, EdwardsProjective}; 15 | pub use element::{AffinePoint, Element}; 16 | pub use encoding::Encoding; 17 | 18 | mod on_curve; 19 | 20 | #[cfg(feature = "r1cs")] 21 | pub mod r1cs; 22 | 23 | pub use bls12_377::Bls12_377; 24 | -------------------------------------------------------------------------------- /src/ark_curve/on_curve.rs: -------------------------------------------------------------------------------- 1 | use ark_ec::{ 2 | models::{twisted_edwards::Projective, twisted_edwards::TECurveConfig}, 3 | Group, 4 | }; 5 | use ark_ff::{BigInteger, Field, PrimeField, Zero}; 6 | use ark_serialize::CanonicalSerialize; 7 | 8 | use crate::ark_curve::constants; 9 | 10 | pub trait OnCurve { 11 | fn is_on_curve(&self) -> bool; 12 | } 13 | 14 | #[cfg(feature = "arkworks")] 15 | impl OnCurve for Projective

{ 16 | #[allow(non_snake_case)] 17 | fn is_on_curve(&self) -> bool { 18 | let XX = self.x.square(); 19 | let YY = self.y.square(); 20 | let ZZ = self.z.square(); 21 | let TT = self.t.square(); 22 | 23 | let on_curve = (YY + P::COEFF_A * XX) == (ZZ + P::COEFF_D * TT); 24 | let on_segre_embedding = self.t * self.z == self.x * self.y; 25 | let z_non_zero = self.z != P::BaseField::zero(); 26 | let point_order_2r = { 27 | let mut r_bytes = [0u8; 32]; 28 | (*constants::R) 29 | .serialize_compressed(&mut r_bytes[..]) 30 | .expect("serialization into array should be infallible"); 31 | let r = P::ScalarField::from_le_bytes_mod_order(&r_bytes); 32 | let mut two_r_bigint = r.into_bigint(); 33 | two_r_bigint.mul2(); 34 | self.mul_bigint(two_r_bigint) == Projective::zero() 35 | }; 36 | 37 | on_curve && on_segre_embedding && z_non_zero && point_order_2r 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ark_curve/ops.rs: -------------------------------------------------------------------------------- 1 | /// Group operations in affine coordinates. 2 | pub mod affine; 3 | /// Group operations in projective coordinates. 4 | pub mod projective; 5 | -------------------------------------------------------------------------------- /src/ark_curve/ops/affine.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 2 | 3 | use ark_ec::twisted_edwards::Projective; 4 | 5 | use crate::{ 6 | ark_curve::element::AffinePoint, ark_curve::Decaf377EdwardsConfig, ark_curve::Element, Fr, 7 | }; 8 | 9 | impl<'a, 'b> Add<&'b AffinePoint> for &'a AffinePoint { 10 | type Output = AffinePoint; 11 | 12 | fn add(self, other: &'b AffinePoint) -> AffinePoint { 13 | AffinePoint { 14 | inner: (self.inner + other.inner).into(), 15 | } 16 | } 17 | } 18 | 19 | impl<'b> Add<&'b AffinePoint> for AffinePoint { 20 | type Output = Element; 21 | 22 | fn add(self, other: &'b AffinePoint) -> Element { 23 | (&self + other).into() 24 | } 25 | } 26 | 27 | impl<'a> Add for &'a AffinePoint { 28 | type Output = AffinePoint; 29 | fn add(self, other: AffinePoint) -> AffinePoint { 30 | self + &other 31 | } 32 | } 33 | 34 | impl<'b> AddAssign<&'b AffinePoint> for AffinePoint { 35 | fn add_assign(&mut self, other: &'b AffinePoint) { 36 | *self = AffinePoint { 37 | inner: (self.inner + other.inner).into(), 38 | } 39 | } 40 | } 41 | 42 | impl AddAssign for AffinePoint { 43 | fn add_assign(&mut self, other: AffinePoint) { 44 | *self += &other; 45 | } 46 | } 47 | 48 | impl<'a, 'b> Sub<&'b AffinePoint> for &'a AffinePoint { 49 | type Output = AffinePoint; 50 | 51 | fn sub(self, other: &'b AffinePoint) -> AffinePoint { 52 | AffinePoint { 53 | inner: (self.inner - other.inner).into(), 54 | } 55 | } 56 | } 57 | 58 | impl<'b> Sub<&'b AffinePoint> for AffinePoint { 59 | type Output = AffinePoint; 60 | 61 | fn sub(self, other: &'b AffinePoint) -> AffinePoint { 62 | &self - other 63 | } 64 | } 65 | 66 | impl<'a> Sub for &'a AffinePoint { 67 | type Output = AffinePoint; 68 | 69 | fn sub(self, other: AffinePoint) -> AffinePoint { 70 | self - &other 71 | } 72 | } 73 | 74 | impl Sub for AffinePoint { 75 | type Output = AffinePoint; 76 | 77 | fn sub(self, other: AffinePoint) -> AffinePoint { 78 | &self - &other 79 | } 80 | } 81 | 82 | impl<'b> SubAssign<&'b AffinePoint> for AffinePoint { 83 | fn sub_assign(&mut self, other: &'b AffinePoint) { 84 | *self = AffinePoint { 85 | inner: (self.inner - other.inner).into(), 86 | } 87 | } 88 | } 89 | 90 | impl SubAssign for AffinePoint { 91 | fn sub_assign(&mut self, other: AffinePoint) { 92 | *self -= &other; 93 | } 94 | } 95 | 96 | impl Neg for AffinePoint { 97 | type Output = Self; 98 | 99 | fn neg(self) -> Self { 100 | AffinePoint { inner: -self.inner } 101 | } 102 | } 103 | 104 | impl<'b> MulAssign<&'b Fr> for AffinePoint { 105 | fn mul_assign(&mut self, point: &'b Fr) { 106 | let mut p: Projective = self.inner.into(); 107 | p *= *point; 108 | *self = AffinePoint { inner: p.into() } 109 | } 110 | } 111 | 112 | impl MulAssign for AffinePoint { 113 | fn mul_assign(&mut self, other: Fr) { 114 | *self *= &other; 115 | } 116 | } 117 | 118 | impl<'a, 'b> Mul<&'b Fr> for &'a AffinePoint { 119 | type Output = AffinePoint; 120 | 121 | fn mul(self, point: &'b Fr) -> AffinePoint { 122 | let mut p: Projective = self.inner.into(); 123 | p *= *point; 124 | AffinePoint { inner: p.into() } 125 | } 126 | } 127 | 128 | impl<'a, 'b> Mul<&'b AffinePoint> for &'a Fr { 129 | type Output = AffinePoint; 130 | 131 | fn mul(self, point: &'b AffinePoint) -> AffinePoint { 132 | point * self 133 | } 134 | } 135 | 136 | impl<'b> Mul<&'b Fr> for AffinePoint { 137 | type Output = Element; 138 | 139 | fn mul(self, other: &'b Fr) -> Element { 140 | (&self * other).into() 141 | } 142 | } 143 | 144 | impl<'a> Mul for &'a AffinePoint { 145 | type Output = AffinePoint; 146 | 147 | fn mul(self, other: Fr) -> AffinePoint { 148 | self * &other 149 | } 150 | } 151 | 152 | impl Mul for AffinePoint { 153 | type Output = Element; 154 | 155 | fn mul(self, other: Fr) -> Element { 156 | (&self * &other).into() 157 | } 158 | } 159 | 160 | impl<'b> Mul<&'b AffinePoint> for Fr { 161 | type Output = AffinePoint; 162 | 163 | fn mul(self, other: &'b AffinePoint) -> AffinePoint { 164 | &self * other 165 | } 166 | } 167 | 168 | impl<'a> Mul for &'a Fr { 169 | type Output = AffinePoint; 170 | 171 | fn mul(self, other: AffinePoint) -> AffinePoint { 172 | self * &other 173 | } 174 | } 175 | 176 | impl Mul for Fr { 177 | type Output = AffinePoint; 178 | 179 | fn mul(self, other: AffinePoint) -> AffinePoint { 180 | &self * &other 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/ark_curve/ops/projective.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 2 | 3 | use crate::{ark_curve::element::projective::Element, ark_curve::AffinePoint, Fr}; 4 | 5 | impl<'a, 'b> Add<&'b Element> for &'a Element { 6 | type Output = Element; 7 | 8 | fn add(self, other: &'b Element) -> Element { 9 | Element { 10 | inner: self.inner + other.inner, 11 | } 12 | } 13 | } 14 | 15 | impl<'b> Add<&'b Element> for Element { 16 | type Output = Element; 17 | fn add(self, other: &'b Element) -> Element { 18 | &self + other 19 | } 20 | } 21 | 22 | impl<'a> Add for &'a Element { 23 | type Output = Element; 24 | fn add(self, other: Element) -> Element { 25 | self + &other 26 | } 27 | } 28 | 29 | impl Add for Element { 30 | type Output = Element; 31 | fn add(self, other: Element) -> Element { 32 | &self + &other 33 | } 34 | } 35 | 36 | impl<'b> AddAssign<&'b Element> for Element { 37 | fn add_assign(&mut self, other: &'b Element) { 38 | *self = Element { 39 | inner: self.inner + other.inner, 40 | } 41 | } 42 | } 43 | 44 | impl AddAssign for Element { 45 | fn add_assign(&mut self, other: Element) { 46 | *self += &other; 47 | } 48 | } 49 | 50 | impl<'a, 'b> Sub<&'b Element> for &'a Element { 51 | type Output = Element; 52 | 53 | fn sub(self, other: &'b Element) -> Element { 54 | Element { 55 | inner: self.inner - other.inner, 56 | } 57 | } 58 | } 59 | 60 | impl<'b> Sub<&'b Element> for Element { 61 | type Output = Element; 62 | 63 | fn sub(self, other: &'b Element) -> Element { 64 | &self - other 65 | } 66 | } 67 | 68 | impl<'a> Sub for &'a Element { 69 | type Output = Element; 70 | 71 | fn sub(self, other: Element) -> Element { 72 | self - &other 73 | } 74 | } 75 | 76 | impl Sub for Element { 77 | type Output = Element; 78 | 79 | fn sub(self, other: Element) -> Element { 80 | &self - &other 81 | } 82 | } 83 | 84 | impl<'b> SubAssign<&'b Element> for Element { 85 | fn sub_assign(&mut self, other: &'b Element) { 86 | *self = Element { 87 | inner: self.inner - other.inner, 88 | } 89 | } 90 | } 91 | 92 | impl SubAssign for Element { 93 | fn sub_assign(&mut self, other: Element) { 94 | *self -= &other; 95 | } 96 | } 97 | 98 | impl Neg for Element { 99 | type Output = Self; 100 | 101 | fn neg(self) -> Self { 102 | Element { inner: -self.inner } 103 | } 104 | } 105 | 106 | impl<'b> MulAssign<&'b Fr> for Element { 107 | // Scalar multiplication is performed through the implementation 108 | // of `MulAssign` on `ProjectiveDecaf377` which is a type alias for 109 | // `Group`. 110 | fn mul_assign(&mut self, point: &'b Fr) { 111 | let mut p = self.inner; 112 | p *= *point; 113 | *self = Element { inner: p } 114 | } 115 | } 116 | 117 | impl MulAssign for Element { 118 | fn mul_assign(&mut self, other: Fr) { 119 | *self *= &other; 120 | } 121 | } 122 | 123 | impl<'a, 'b> Mul<&'b Fr> for &'a Element { 124 | type Output = Element; 125 | 126 | fn mul(self, point: &'b Fr) -> Element { 127 | let mut p = self.inner; 128 | p *= *point; 129 | Element { inner: p } 130 | } 131 | } 132 | 133 | impl<'a, 'b> Mul<&'b Element> for &'a Fr { 134 | type Output = Element; 135 | 136 | fn mul(self, point: &'b Element) -> Element { 137 | point * self 138 | } 139 | } 140 | 141 | impl<'b> Mul<&'b Fr> for Element { 142 | type Output = Element; 143 | 144 | fn mul(self, other: &'b Fr) -> Element { 145 | &self * other 146 | } 147 | } 148 | 149 | impl<'a> Mul for &'a Element { 150 | type Output = Element; 151 | 152 | fn mul(self, other: Fr) -> Element { 153 | self * &other 154 | } 155 | } 156 | 157 | impl Mul for Element { 158 | type Output = Element; 159 | 160 | fn mul(self, other: Fr) -> Element { 161 | &self * &other 162 | } 163 | } 164 | 165 | impl<'b> Mul<&'b Element> for Fr { 166 | type Output = Element; 167 | 168 | fn mul(self, other: &'b Element) -> Element { 169 | &self * other 170 | } 171 | } 172 | 173 | impl<'a> Mul for &'a Fr { 174 | type Output = Element; 175 | 176 | fn mul(self, other: Element) -> Element { 177 | self * &other 178 | } 179 | } 180 | 181 | impl Mul for Fr { 182 | type Output = Element; 183 | 184 | fn mul(self, other: Element) -> Element { 185 | &self * &other 186 | } 187 | } 188 | 189 | impl<'a> Add<&'a AffinePoint> for Element { 190 | type Output = Element; 191 | 192 | fn add(self, other: &'a AffinePoint) -> Element { 193 | let element_other: Element = other.into(); 194 | &self + element_other 195 | } 196 | } 197 | 198 | impl Add for Element { 199 | type Output = Element; 200 | 201 | fn add(self, other: AffinePoint) -> Element { 202 | &self + &other.into() 203 | } 204 | } 205 | 206 | impl Add for AffinePoint { 207 | // Required to be `Element` to satisfy type bounds on 208 | // `AffineRepr::Group`. 209 | type Output = Element; 210 | 211 | fn add(self, other: AffinePoint) -> Element { 212 | let other_element: Element = other.into(); 213 | let self_element: Element = other.into(); 214 | self_element + other_element 215 | } 216 | } 217 | 218 | impl Add for AffinePoint { 219 | type Output = Element; 220 | 221 | fn add(self, other: Element) -> Element { 222 | &self.into() + &other 223 | } 224 | } 225 | 226 | impl<'a> Add<&'a Element> for AffinePoint { 227 | type Output = Element; 228 | 229 | fn add(self, other: &'a Element) -> Element { 230 | &self.into() + other 231 | } 232 | } 233 | 234 | impl<'a> AddAssign<&'a AffinePoint> for Element { 235 | fn add_assign(&mut self, other: &'a AffinePoint) { 236 | let other_element: Element = other.into(); 237 | *self += other_element; 238 | } 239 | } 240 | 241 | impl AddAssign for Element { 242 | fn add_assign(&mut self, other: AffinePoint) { 243 | *self += &other; 244 | } 245 | } 246 | 247 | impl<'a> SubAssign<&'a AffinePoint> for Element { 248 | fn sub_assign(&mut self, other: &'a AffinePoint) { 249 | let other_element: Element = other.into(); 250 | *self -= other_element; 251 | } 252 | } 253 | 254 | impl SubAssign for Element { 255 | fn sub_assign(&mut self, other: AffinePoint) { 256 | let other_element: Element = other.into(); 257 | *self -= other_element; 258 | } 259 | } 260 | 261 | impl<'a> Sub<&'a AffinePoint> for Element { 262 | type Output = Element; 263 | 264 | fn sub(self, other: &'a AffinePoint) -> Element { 265 | let other_element: Element = other.into(); 266 | &self - other_element 267 | } 268 | } 269 | 270 | impl Sub for Element { 271 | type Output = Element; 272 | 273 | fn sub(self, other: AffinePoint) -> Element { 274 | &self - &other.into() 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/ark_curve/r1cs.rs: -------------------------------------------------------------------------------- 1 | pub mod element; 2 | pub mod fqvar_ext; 3 | mod inner; 4 | mod lazy; 5 | pub mod ops; 6 | 7 | use ark_ff::ToConstraintField; 8 | use ark_std::vec::Vec; 9 | pub use element::ElementVar; 10 | 11 | use crate::{Element, Fq}; 12 | use ark_r1cs_std::fields::fp::FpVar; 13 | use ark_relations::r1cs::{ 14 | ConstraintSynthesizer, ConstraintSystem, OptimizationGoal, SynthesisMode, 15 | }; 16 | 17 | pub type FqVar = FpVar; 18 | 19 | pub trait CountConstraints: ConstraintSynthesizer + Sized { 20 | fn num_constraints_and_instance_variables(self) -> (usize, usize) { 21 | let cs = ConstraintSystem::new_ref(); 22 | cs.set_optimization_goal(OptimizationGoal::Constraints); 23 | cs.set_mode(SynthesisMode::Setup); 24 | 25 | // Synthesize the circuit. 26 | self.generate_constraints(cs.clone()) 27 | .expect("can generate constraints"); 28 | cs.finalize(); 29 | (cs.num_constraints(), cs.num_instance_variables()) 30 | } 31 | } 32 | 33 | impl CountConstraints for T where T: ConstraintSynthesizer + Sized {} 34 | 35 | impl ToConstraintField for Element { 36 | fn to_field_elements(&self) -> Option> { 37 | Some([self.vartime_compress_to_field()].to_vec()) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/ark_curve/r1cs/element.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | use core::borrow::Borrow; 3 | 4 | use ark_ec::AffineRepr; 5 | use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, prelude::*, R1CSVar}; 6 | use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; 7 | use ark_std::vec::Vec; 8 | 9 | use crate::ark_curve::r1cs::{lazy::LazyElementVar, FqVar}; 10 | use crate::ark_curve::{edwards::EdwardsAffine, r1cs::inner::ElementVar as InnerElementVar}; 11 | use crate::ark_curve::{AffinePoint, Element}; 12 | use crate::Fq; 13 | 14 | use super::inner::Decaf377EdwardsVar; 15 | 16 | #[derive(Clone, Debug)] 17 | /// Represents the R1CS equivalent of a `decaf377::Element` 18 | /// 19 | /// Generally the suffix -`Var` will indicate that the type or variable 20 | /// represents in R1CS. 21 | pub struct ElementVar { 22 | pub(crate) inner: LazyElementVar, 23 | } 24 | 25 | impl ElementVar { 26 | /// R1CS equivalent of `Element::vartime_compress_to_field` 27 | pub fn compress_to_field(&self) -> Result { 28 | self.inner.encoding() 29 | } 30 | 31 | /// R1CS equivalent of `Encoding::vartime_decompress` 32 | pub fn decompress_from_field(s_var: FqVar) -> Result { 33 | let inner = LazyElementVar::new_from_encoding(s_var); 34 | // This enforces that the encoding is valid. 35 | inner.element()?; 36 | Ok(Self { inner }) 37 | } 38 | 39 | /// R1CS equivalent of `Element::elligator_map` 40 | pub(crate) fn elligator_map(r_0_var: &FqVar) -> Result { 41 | let inner = InnerElementVar::elligator_map(r_0_var)?; 42 | Ok(Self { 43 | inner: LazyElementVar::new_from_element(inner), 44 | }) 45 | } 46 | 47 | /// Maps a field element to a decaf377 `ElementVar` suitable for CDH challenges. 48 | pub fn encode_to_curve(r_var: &FqVar) -> Result { 49 | Self::elligator_map(r_var) 50 | } 51 | } 52 | 53 | impl EqGadget for ElementVar { 54 | fn is_eq(&self, other: &Self) -> Result, SynthesisError> { 55 | self.inner.element()?.is_eq(&other.inner.element()?) 56 | } 57 | 58 | fn conditional_enforce_equal( 59 | &self, 60 | other: &Self, 61 | should_enforce: &Boolean, 62 | ) -> Result<(), SynthesisError> { 63 | // should_enforce = true 64 | // return self == other 65 | // should_enforce = false 66 | // return true 67 | self.is_eq(other)? 68 | .conditional_enforce_equal(&Boolean::constant(true), should_enforce) 69 | } 70 | 71 | fn conditional_enforce_not_equal( 72 | &self, 73 | other: &Self, 74 | should_enforce: &Boolean, 75 | ) -> Result<(), SynthesisError> { 76 | self.is_eq(other)? 77 | .conditional_enforce_equal(&Boolean::constant(false), should_enforce) 78 | } 79 | } 80 | 81 | impl R1CSVar for ElementVar { 82 | type Value = Element; 83 | 84 | fn cs(&self) -> ConstraintSystemRef { 85 | let inner = self 86 | .inner 87 | .element() 88 | .expect("element will exist if ElementVar is allocated"); 89 | inner.cs() 90 | } 91 | 92 | fn value(&self) -> Result { 93 | let inner_element = self.inner.element()?; 94 | let (x, y) = ( 95 | inner_element.inner.x.value()?, 96 | inner_element.inner.y.value()?, 97 | ); 98 | let result = EdwardsAffine::new(x, y); 99 | 100 | Ok(Element { 101 | inner: result.into(), 102 | }) 103 | } 104 | } 105 | 106 | impl CondSelectGadget for ElementVar { 107 | fn conditionally_select( 108 | cond: &Boolean, 109 | true_value: &Self, 110 | false_value: &Self, 111 | ) -> Result { 112 | let true_element = true_value.inner.element()?; 113 | let false_element = false_value.inner.element()?; 114 | let x = cond.select(&true_element.inner.x, &false_element.inner.x)?; 115 | let y = cond.select(&true_element.inner.y, &false_element.inner.y)?; 116 | 117 | let new_element = InnerElementVar { 118 | inner: Decaf377EdwardsVar::new(x, y), 119 | }; 120 | Ok(Self { 121 | inner: LazyElementVar::new_from_element(new_element), 122 | }) 123 | } 124 | } 125 | 126 | // This lets us use `new_constant`, `new_input` (public), or `new_witness` to add 127 | // decaf elements to an R1CS constraint system. 128 | impl AllocVar for ElementVar { 129 | fn new_variable>( 130 | cs: impl Into>, 131 | f: impl FnOnce() -> Result, 132 | mode: AllocationMode, 133 | ) -> Result { 134 | let ns = cs.into(); 135 | let cs = ns.cs(); 136 | match mode { 137 | AllocationMode::Input => { 138 | let value: Element = *f()?.borrow(); 139 | let compressed = value.vartime_compress_to_field(); 140 | Ok(Self::new_input(cs, || Ok(compressed))?) 141 | } 142 | _ => { 143 | let inner = InnerElementVar::new_variable(cs, f, mode)?; 144 | Ok(Self { 145 | inner: LazyElementVar::new_from_element(inner), 146 | }) 147 | } 148 | } 149 | } 150 | } 151 | 152 | impl AllocVar for ElementVar { 153 | fn new_variable>( 154 | cs: impl Into>, 155 | f: impl FnOnce() -> Result, 156 | mode: AllocationMode, 157 | ) -> Result { 158 | Self::new_variable(cs, || f().map(|b| b.borrow().into_group()), mode) 159 | } 160 | } 161 | 162 | impl AllocVar for ElementVar { 163 | fn new_variable>( 164 | cs: impl Into>, 165 | f: impl FnOnce() -> Result, 166 | mode: AllocationMode, 167 | ) -> Result { 168 | let ns = cs.into(); 169 | let cs = ns.cs(); 170 | let compressed = FqVar::new_variable(cs, f, mode)?; 171 | Ok(Self { 172 | inner: LazyElementVar::new_from_encoding(compressed), 173 | }) 174 | } 175 | } 176 | 177 | impl ToBitsGadget for ElementVar { 178 | fn to_bits_le(&self) -> Result>, SynthesisError> { 179 | let compressed_fq = self 180 | .inner 181 | .element() 182 | .expect("element will exist") 183 | .to_bits_le()?; 184 | let encoded_bits = compressed_fq.to_bits_le()?; 185 | Ok(encoded_bits) 186 | } 187 | } 188 | 189 | impl ToBytesGadget for ElementVar { 190 | fn to_bytes(&self) -> Result>, SynthesisError> { 191 | let compressed_fq = self 192 | .inner 193 | .element() 194 | .expect("element will exist") 195 | .to_bytes()?; 196 | let encoded_bytes = compressed_fq.to_bytes()?; 197 | Ok(encoded_bytes) 198 | } 199 | } 200 | 201 | impl<'a> GroupOpsBounds<'a, Element, ElementVar> for ElementVar {} 202 | 203 | impl CurveVar for ElementVar { 204 | fn zero() -> Self { 205 | let new_element = InnerElementVar::zero(); 206 | Self { 207 | inner: LazyElementVar::new_from_element(new_element), 208 | } 209 | } 210 | 211 | fn constant(other: Element) -> Self { 212 | let new_element = InnerElementVar::constant(other); 213 | Self { 214 | inner: LazyElementVar::new_from_element(new_element), 215 | } 216 | } 217 | 218 | fn new_variable_omit_prime_order_check( 219 | cs: impl Into>, 220 | f: impl FnOnce() -> Result, 221 | mode: AllocationMode, 222 | ) -> Result { 223 | let ns = cs.into(); 224 | let cs = ns.cs(); 225 | 226 | match f() { 227 | Ok(ge) => { 228 | let new_element = 229 | InnerElementVar::new_variable_omit_prime_order_check(cs, || Ok(ge), mode)?; 230 | Ok(Self { 231 | inner: LazyElementVar::new_from_element(new_element), 232 | }) 233 | } 234 | _ => Err(SynthesisError::AssignmentMissing), 235 | } 236 | } 237 | 238 | fn enforce_prime_order(&self) -> Result<(), SynthesisError> { 239 | // This is decaf 240 | Ok(()) 241 | } 242 | 243 | fn double_in_place(&mut self) -> Result<(), SynthesisError> { 244 | let mut inner_element = self.inner.element().expect("element will exist"); 245 | inner_element.double_in_place()?; 246 | *self = Self { 247 | inner: LazyElementVar::new_from_element(inner_element), 248 | }; 249 | Ok(()) 250 | } 251 | 252 | fn negate(&self) -> Result { 253 | let negated = self.inner.element().expect("element will exist").negate()?; 254 | Ok(Self { 255 | inner: LazyElementVar::new_from_element(negated), 256 | }) 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/ark_curve/r1cs/fqvar_ext.rs: -------------------------------------------------------------------------------- 1 | use ark_r1cs_std::eq::EqGadget; 2 | use ark_r1cs_std::prelude::{AllocVar, Boolean, FieldVar}; 3 | use ark_r1cs_std::select::CondSelectGadget; 4 | use ark_r1cs_std::{R1CSVar, ToBitsGadget}; 5 | use ark_relations::r1cs::SynthesisError; 6 | 7 | use crate::ark_curve::{constants::ZETA, r1cs::FqVar}; 8 | use crate::Fq; 9 | 10 | pub trait FqVarExtension: Sized { 11 | fn isqrt(&self) -> Result<(Boolean, FqVar), SynthesisError>; 12 | 13 | // This is similar to the Sign trait in this crate, 14 | // however: we need to return `Result<_, SynthesisError>` 15 | // everywhere. 16 | fn is_negative(&self) -> Result, SynthesisError>; 17 | fn is_nonnegative(&self) -> Result, SynthesisError>; 18 | fn abs(self) -> Result; 19 | } 20 | 21 | impl FqVarExtension for FqVar { 22 | /// Inverse square root in R1CS 23 | /// 24 | /// Cases: 25 | /// - Case 1: `(true, sqrt(num/den))` if `num` and `den` are both nonzero and `num/den` is square; 26 | /// - Case 2: `(true, 0)` if `num` is zero; 27 | /// - Case 3: `(false, 0)` if `den` is zero; 28 | /// - Case 4: `(false, sqrt(zeta*num/den))` if `num` and `den` are both nonzero and `num/den` is nonsquare; 29 | fn isqrt(&self) -> Result<(Boolean, FqVar), SynthesisError> { 30 | // During mode `SynthesisMode::Setup`, value() will not provide a field element. 31 | let den = self.value().unwrap_or(Fq::ONE); 32 | 33 | // Out of circuit sqrt computation: 34 | // Note: `num = 1` 35 | // `y = sqrt(num/den)` 36 | let (was_square, y) = Fq::sqrt_ratio_zeta(&Fq::ONE, &den); 37 | 38 | let cs = self.cs(); 39 | let was_square_var = Boolean::new_witness(cs.clone(), || Ok(was_square))?; 40 | let y_var = FqVar::new_witness(cs.clone(), || Ok(y))?; 41 | // `y^2 = num/den` 42 | let y_squared_var = y_var.square()?; 43 | 44 | // The below is a flattened version of the four cases above, excluding case 2 since `num` is hardcoded 45 | // to be one. 46 | // 47 | // Case 1: `(true, sqrt(num/den))` if `num` and `den` are both nonzero and `num/den` is square 48 | let den_var_is_zero = self.is_eq(&FqVar::zero())?; 49 | let den_var = FqVar::conditionally_select(&den_var_is_zero, &FqVar::one(), self)?; 50 | let den_var_inv = den_var.inverse()?; 51 | // Note we could be in case 1 or case 2 based on the constraint that `was_square = true`, but 52 | // num is hardcoded to be one above, so we're in case 1. 53 | let in_case_1 = was_square_var.clone(); 54 | // Certify the return value y is sqrt(1/den) when we're in case 1. This also certifies that we are not in case 2. 55 | y_squared_var.conditional_enforce_equal(&den_var_inv, &in_case_1)?; 56 | 57 | // Case 3: `(false, 0)` if `den` is zero 58 | let was_not_square_var = was_square_var.not(); 59 | let in_case_3 = was_not_square_var.and(&den_var_is_zero)?; 60 | // Certify the return value y is 0 when we're in case 3. 61 | y_squared_var.conditional_enforce_equal(&FqVar::zero(), &in_case_3)?; 62 | 63 | // Case 4: `(false, sqrt(zeta*num/den))` if `num` and `den` are both nonzero and `num/den` is nonsquare; 64 | let zeta_var = FqVar::new_constant(cs, ZETA)?; 65 | let zeta_times_one_over_den_var = zeta_var * den_var_inv; 66 | let in_case_4 = was_not_square_var.and(&den_var_is_zero.not())?; 67 | // Certify the return value y is sqrt(zeta * 1/den) 68 | y_squared_var.conditional_enforce_equal(&zeta_times_one_over_den_var, &in_case_4)?; 69 | 70 | // Ensure that we are in case 1, 3, or 4. 71 | let in_case = in_case_1.or(&in_case_3)?.or(&in_case_4)?; 72 | in_case.enforce_equal(&Boolean::TRUE)?; 73 | 74 | Ok((was_square_var, y_var)) 75 | } 76 | 77 | fn is_negative(&self) -> Result, SynthesisError> { 78 | Ok(self.is_nonnegative()?.not()) 79 | } 80 | 81 | fn is_nonnegative(&self) -> Result, SynthesisError> { 82 | let bitvars = self.to_bits_le()?; 83 | 84 | // bytes[0] & 1 == 0 85 | let true_var = Boolean::::TRUE; 86 | let false_var = Boolean::::FALSE; 87 | 88 | // Check least significant bit 89 | let lhs = bitvars[0].and(&true_var)?; 90 | let is_nonnegative_var = lhs.is_eq(&false_var)?; 91 | 92 | Ok(is_nonnegative_var) 93 | } 94 | 95 | fn abs(self) -> Result { 96 | let absolute_value = 97 | FqVar::conditionally_select(&self.is_nonnegative()?, &self, &self.negate()?)?; 98 | Ok(absolute_value) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/ark_curve/r1cs/lazy.rs: -------------------------------------------------------------------------------- 1 | use core::cell::RefCell; 2 | 3 | use ark_relations::r1cs::SynthesisError; 4 | 5 | use super::inner::ElementVar; 6 | use crate::ark_curve::r1cs::FqVar; 7 | 8 | #[derive(Clone, Debug)] 9 | pub enum Inner { 10 | Encoding(FqVar), 11 | Element(ElementVar), 12 | EncodingAndElement { 13 | encoding: FqVar, 14 | element: ElementVar, 15 | }, 16 | } 17 | 18 | #[derive(Clone, Debug)] 19 | pub struct LazyElementVar { 20 | inner: RefCell, 21 | } 22 | 23 | impl LazyElementVar { 24 | pub fn new_from_element(element: ElementVar) -> Self { 25 | Self { 26 | inner: RefCell::new(Inner::Element(element)), 27 | } 28 | } 29 | 30 | pub fn new_from_encoding(encoding: FqVar) -> Self { 31 | Self { 32 | inner: RefCell::new(Inner::Encoding(encoding)), 33 | } 34 | } 35 | 36 | pub fn element(&self) -> Result { 37 | if matches!(&*self.inner.borrow(), Inner::Encoding(_)) { 38 | let encoding = self.encoding()?; 39 | let element = ElementVar::decompress_from_field(encoding.clone())?; 40 | *self.inner.borrow_mut() = Inner::EncodingAndElement { encoding, element }; 41 | } 42 | match &*self.inner.borrow() { 43 | Inner::Encoding(_) => { 44 | unreachable!("encoding should have been replaced by encoding and element") 45 | } 46 | Inner::Element(element) => Ok(element.clone()), 47 | Inner::EncodingAndElement { element, .. } => Ok(element.clone()), 48 | } 49 | } 50 | 51 | pub fn encoding(&self) -> Result { 52 | if matches!(&*self.inner.borrow(), Inner::Element(_)) { 53 | let element = self.element()?; 54 | let encoding = element.compress_to_field()?; 55 | *self.inner.borrow_mut() = Inner::EncodingAndElement { encoding, element }; 56 | } 57 | match &*self.inner.borrow() { 58 | Inner::Encoding(encoding) => Ok(encoding.clone()), 59 | Inner::Element(_) => { 60 | unreachable!("encoding should have been replaced by encoding and element") 61 | } 62 | Inner::EncodingAndElement { encoding, .. } => Ok(encoding.clone()), 63 | } 64 | } 65 | } 66 | 67 | #[cfg(test)] 68 | mod tests { 69 | use crate::{Bls12_377, Element, Fq}; 70 | use ark_groth16::{r1cs_to_qap::LibsnarkReduction, Groth16, ProvingKey, VerifyingKey}; 71 | use ark_r1cs_std::prelude::AllocVar; 72 | use ark_relations::r1cs::ConstraintSynthesizer; 73 | use ark_snark::SNARK; 74 | use rand_core::OsRng; 75 | 76 | use super::*; 77 | 78 | #[derive(Clone)] 79 | struct TestCircuit { 80 | // Witness 81 | encoding: Fq, 82 | } 83 | 84 | impl ConstraintSynthesizer for TestCircuit { 85 | fn generate_constraints( 86 | self, 87 | cs: ark_relations::r1cs::ConstraintSystemRef, 88 | ) -> ark_relations::r1cs::Result<()> { 89 | let encoding_var = FqVar::new_witness(cs, || Ok(self.encoding))?; 90 | let lazy_var = LazyElementVar::new_from_encoding(encoding_var); 91 | let _element_var = lazy_var.element()?; 92 | Ok(()) 93 | } 94 | } 95 | 96 | impl TestCircuit { 97 | fn generate_test_parameters() -> (ProvingKey, VerifyingKey) { 98 | let element = Element::default(); 99 | let encoding = element.vartime_compress_to_field(); 100 | let circuit = TestCircuit { encoding }; 101 | let (pk, vk) = Groth16::::circuit_specific_setup( 102 | circuit, &mut OsRng, 103 | ) 104 | .expect("can perform circuit specific setup"); 105 | (pk, vk) 106 | } 107 | } 108 | 109 | #[test] 110 | fn lazy_element_var_evaluation() { 111 | let (pk, _) = TestCircuit::generate_test_parameters(); 112 | let mut rng = OsRng; 113 | let test_circuit = TestCircuit { 114 | encoding: Element::default().vartime_compress_to_field(), 115 | }; 116 | Groth16::::prove(&pk, test_circuit, &mut rng) 117 | .expect("can generate proof"); 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/ark_curve/r1cs/ops.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, AddAssign, Sub, SubAssign}; 2 | 3 | use crate::ark_curve::{r1cs::element::ElementVar, Element}; 4 | 5 | use super::lazy::LazyElementVar; 6 | 7 | impl Add for ElementVar { 8 | type Output = ElementVar; 9 | 10 | fn add(self, other: ElementVar) -> Self::Output { 11 | ElementVar { 12 | inner: LazyElementVar::new_from_element( 13 | self.inner 14 | .element() 15 | .expect("element will exist") 16 | .add(other.inner.element().expect("element will exist")), 17 | ), 18 | } 19 | } 20 | } 21 | 22 | impl<'a> Add<&'a ElementVar> for ElementVar { 23 | type Output = ElementVar; 24 | 25 | fn add(self, other: &'a ElementVar) -> Self::Output { 26 | ElementVar { 27 | inner: LazyElementVar::new_from_element( 28 | self.inner 29 | .element() 30 | .expect("element will exist") 31 | .add(other.inner.element().expect("element will exist")), 32 | ), 33 | } 34 | } 35 | } 36 | 37 | impl AddAssign for ElementVar { 38 | fn add_assign(&mut self, rhs: ElementVar) { 39 | let rhs = rhs.inner.element().expect("element will exist"); 40 | let mut lhs = self.inner.element().expect("element will exist"); 41 | lhs.add_assign(rhs); 42 | 43 | *self = ElementVar { 44 | inner: LazyElementVar::new_from_element(lhs), 45 | }; 46 | } 47 | } 48 | 49 | impl<'a> AddAssign<&'a ElementVar> for ElementVar { 50 | fn add_assign(&mut self, rhs: &'a ElementVar) { 51 | let rhs = rhs.inner.element().expect("element will exist"); 52 | let mut lhs = self.inner.element().expect("element will exist"); 53 | lhs.add_assign(rhs); 54 | 55 | *self = ElementVar { 56 | inner: LazyElementVar::new_from_element(lhs), 57 | }; 58 | } 59 | } 60 | 61 | impl Sub for ElementVar { 62 | type Output = ElementVar; 63 | 64 | fn sub(self, other: ElementVar) -> Self::Output { 65 | ElementVar { 66 | inner: LazyElementVar::new_from_element( 67 | self.inner 68 | .element() 69 | .expect("element will exist") 70 | .sub(other.inner.element().expect("element will exist")), 71 | ), 72 | } 73 | } 74 | } 75 | 76 | impl<'a> Sub<&'a ElementVar> for ElementVar { 77 | type Output = ElementVar; 78 | 79 | fn sub(self, other: &'a ElementVar) -> Self::Output { 80 | ElementVar { 81 | inner: LazyElementVar::new_from_element( 82 | self.inner 83 | .element() 84 | .expect("element will exist") 85 | .sub(other.inner.element().expect("element will exist")), 86 | ), 87 | } 88 | } 89 | } 90 | 91 | impl SubAssign for ElementVar { 92 | fn sub_assign(&mut self, rhs: ElementVar) { 93 | let rhs = rhs.inner.element().expect("element will exist"); 94 | let mut lhs = self.inner.element().expect("element will exist"); 95 | lhs.sub_assign(rhs); 96 | 97 | *self = ElementVar { 98 | inner: LazyElementVar::new_from_element(lhs), 99 | }; 100 | } 101 | } 102 | 103 | impl<'a> SubAssign<&'a ElementVar> for ElementVar { 104 | fn sub_assign(&mut self, rhs: &'a ElementVar) { 105 | let rhs = rhs.inner.element().expect("element will exist"); 106 | let mut lhs = self.inner.element().expect("element will exist"); 107 | lhs.sub_assign(rhs); 108 | 109 | *self = ElementVar { 110 | inner: LazyElementVar::new_from_element(lhs), 111 | }; 112 | } 113 | } 114 | 115 | impl Sub for ElementVar { 116 | type Output = ElementVar; 117 | 118 | fn sub(self, other: Element) -> Self::Output { 119 | ElementVar { 120 | inner: LazyElementVar::new_from_element( 121 | self.inner.element().expect("element will exist").sub(other), 122 | ), 123 | } 124 | } 125 | } 126 | 127 | impl SubAssign for ElementVar { 128 | fn sub_assign(&mut self, rhs: Element) { 129 | let mut lhs = self.inner.element().expect("element will exist"); 130 | lhs.sub_assign(rhs); 131 | 132 | *self = ElementVar { 133 | inner: LazyElementVar::new_from_element(lhs), 134 | }; 135 | } 136 | } 137 | 138 | impl Add for ElementVar { 139 | type Output = ElementVar; 140 | 141 | fn add(self, other: Element) -> Self::Output { 142 | ElementVar { 143 | inner: LazyElementVar::new_from_element( 144 | self.inner.element().expect("element will exist").add(other), 145 | ), 146 | } 147 | } 148 | } 149 | 150 | impl AddAssign for ElementVar { 151 | fn add_assign(&mut self, rhs: Element) { 152 | let mut lhs = self.inner.element().expect("element will exist"); 153 | lhs.add_assign(rhs); 154 | 155 | *self = ElementVar { 156 | inner: LazyElementVar::new_from_element(lhs), 157 | }; 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/ark_curve/rand.rs: -------------------------------------------------------------------------------- 1 | use ark_ff::UniformRand; 2 | use ark_serialize::CanonicalSerialize; 3 | use ark_std::rand::{ 4 | distributions::{Distribution, Standard}, 5 | Rng, 6 | }; 7 | 8 | use crate::ark_curve::{edwards::EdwardsProjective, AffinePoint, Element, Encoding}; 9 | 10 | impl Distribution for Standard { 11 | #[inline] 12 | fn sample(&self, rng: &mut R) -> Element { 13 | loop { 14 | // 1. Generate random underlying elliptic curve point. 15 | let test_point = EdwardsProjective::rand(rng); 16 | let mut bytes = [0u8; 32]; 17 | test_point 18 | .serialize_compressed(&mut bytes[..]) 19 | .expect("serialization into array should be infallible"); 20 | 21 | // 2. Check if valid decaf point. If not continue. 22 | if let Ok(p) = Encoding(bytes).vartime_decompress() { 23 | return p; 24 | } 25 | } 26 | } 27 | } 28 | 29 | impl Distribution for Standard { 30 | fn sample(&self, rng: &mut R) -> AffinePoint { 31 | let point: Element = self.sample(rng); 32 | point.into() 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/ark_curve/serialize.rs: -------------------------------------------------------------------------------- 1 | use core::convert::TryInto; 2 | 3 | use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; 4 | use ark_std::io::{Read, Write}; 5 | 6 | use crate::ark_curve::{AffinePoint, Element, Encoding}; 7 | 8 | impl CanonicalDeserialize for AffinePoint { 9 | fn deserialize_with_mode( 10 | reader: R, 11 | compress: ark_serialize::Compress, 12 | validate: ark_serialize::Validate, 13 | ) -> Result { 14 | match compress { 15 | ark_serialize::Compress::Yes => (), 16 | ark_serialize::Compress::No => unimplemented!(), 17 | }; 18 | match validate { 19 | ark_serialize::Validate::Yes => (), 20 | ark_serialize::Validate::No => unimplemented!(), 21 | } 22 | let bytes = Encoding::deserialize_compressed(reader)?; 23 | let element: Element = bytes 24 | .try_into() 25 | .map_err(|_| ark_serialize::SerializationError::InvalidData)?; 26 | Ok(element.into()) 27 | } 28 | } 29 | 30 | impl CanonicalSerialize for AffinePoint { 31 | fn serialized_size(&self, compress: ark_serialize::Compress) -> usize { 32 | match compress { 33 | ark_serialize::Compress::Yes => 32, 34 | ark_serialize::Compress::No => unimplemented!(), 35 | } 36 | } 37 | 38 | fn serialize_with_mode( 39 | &self, 40 | writer: W, 41 | mode: ark_serialize::Compress, 42 | ) -> Result<(), ark_serialize::SerializationError> { 43 | let element: Element = self.into(); 44 | element.vartime_compress().serialize_with_mode(writer, mode) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 2 | pub enum EncodingError { 3 | InvalidEncoding, 4 | InvalidSliceLength, 5 | } 6 | 7 | impl core::fmt::Display for EncodingError { 8 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 9 | let msg = match self { 10 | Self::InvalidEncoding => "Invalid Decaf377 encoding", 11 | Self::InvalidSliceLength => "Invalid length bytes in encoded point", 12 | }; 13 | 14 | msg.fmt(f) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/fields.rs: -------------------------------------------------------------------------------- 1 | //! Auto-generated, formally-verified field arithmetic implementations, together 2 | //! with wrapper types that make the generated code actually usable. 3 | //! 4 | //! ## Code Structure 5 | //! 6 | //! We have two backends (`u32` and `u64`) and three fields (`Fp`, `Fq`, and 7 | //! `Fr`), giving six implementations total. This code is organized into submodules: 8 | //! ```ascii,no_run 9 | //! fields::{u32, u64}::{fp, fq, fr}::{ 10 | //! fiat, // generated code goes here 11 | //! wrapper, // wrapper types Fp,Fq,Fr, putting a Rust API on the generated code 12 | //! arkworks, // impls of arkworks traits for the wrapper types 13 | //! } 14 | //! ``` 15 | //! The Fp is a 48-byte field element, while Fq and Fr are 32-byte field elements. 16 | //! The wrapper code is all copy-pasted and should be kept in sync after any edits. 17 | //! The different backends should have identical external interfaces, so they can be 18 | //! used with a cfg-able type alias. 19 | 20 | pub mod fp; 21 | pub mod fq; 22 | pub mod fr; 23 | -------------------------------------------------------------------------------- /src/fields/fp.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | use rand_core::CryptoRngCore; 3 | 4 | use crate::EncodingError; 5 | 6 | #[cfg(feature = "arkworks")] 7 | pub mod arkworks; 8 | mod ops; 9 | mod u32; 10 | 11 | // The u64 backend requires arkworks 12 | #[cfg(feature = "arkworks")] 13 | mod u64; 14 | 15 | cfg_if! { 16 | if #[cfg(feature = "arkworks")] { 17 | pub type Fp = u64::Fp; 18 | } else { 19 | pub type Fp = u32::Fp; 20 | } 21 | } 22 | 23 | const B: usize = 377; 24 | const N_8: usize = (B + 7) / 8; 25 | const N_32: usize = (B + 31) / 32; 26 | const N_64: usize = (B + 63) / 64; 27 | 28 | impl Fp { 29 | pub const MODULUS_LIMBS: [u64; N_64] = [ 30 | 9586122913090633729, 31 | 1660523435060625408, 32 | 2230234197602682880, 33 | 1883307231910630287, 34 | 14284016967150029115, 35 | 121098312706494698, 36 | ]; 37 | 38 | pub const MODULUS_MINUS_ONE_DIV_TWO_LIMBS: [u64; N_64] = [ 39 | 4793061456545316864, 40 | 830261717530312704, 41 | 10338489135656117248, 42 | 10165025652810090951, 43 | 7142008483575014557, 44 | 60549156353247349, 45 | ]; 46 | 47 | pub const MODULUS_BIT_SIZE: u32 = 0x179; 48 | 49 | pub const TRACE_LIMBS: [u64; N_64] = [ 50 | 8435453208297608227, 51 | 9853568280881552429, 52 | 7479357291536088013, 53 | 1657802422768920715, 54 | 16796279350917535980, 55 | 1720, 56 | ]; 57 | 58 | pub const TRACE_MINUS_ONE_DIV_TWO_LIMBS: [u64; N_64] = [ 59 | 13441098641003579921, 60 | 14150156177295552022, 61 | 12963050682622819814, 62 | 828901211384460357, 63 | 8398139675458767990, 64 | 860, 65 | ]; 66 | 67 | pub const TWO_ADICITY: u32 = 0x2e; 68 | 69 | pub const QUADRATIC_NON_RESIDUE_TO_TRACE: Self = Self::from_montgomery_limbs([ 70 | 7563926049028936178, 71 | 2688164645460651601, 72 | 12112688591437172399, 73 | 3177973240564633687, 74 | 14764383749841851163, 75 | 52487407124055189, 76 | ]); 77 | 78 | pub const MULTIPLICATIVE_GENERATOR: Self = Self::from_montgomery_limbs([ 79 | 1580481994230331156, 80 | 7393753505699199837, 81 | 15893201093018099506, 82 | 15064395564155502359, 83 | 7595513421530309810, 84 | 112614884009382239, 85 | ]); 86 | 87 | pub const TWO_ADIC_ROOT_OF_UNITY: Self = Self::from_montgomery_limbs([ 88 | 16125954451488549662, 89 | 8217881455460992412, 90 | 2710394594754331350, 91 | 15576616684900113046, 92 | 13256804877427073124, 93 | 71394035925664393, 94 | ]); 95 | 96 | pub const FIELD_SIZE_POWER_OF_TWO: Self = Self::from_montgomery_limbs([ 97 | 13224372171368877346, 98 | 227991066186625457, 99 | 2496666625421784173, 100 | 13825906835078366124, 101 | 9475172226622360569, 102 | 30958721782860680, 103 | ]); 104 | 105 | pub fn from_le_bytes_mod_order(bytes: &[u8]) -> Self { 106 | bytes 107 | .chunks(N_8) 108 | .map(|x| { 109 | let mut padded = [0u8; N_8]; 110 | padded[..x.len()].copy_from_slice(x); 111 | Self::from_raw_bytes(&padded) 112 | }) // [X, 2^(384) * X, ...] 113 | .rev() 114 | .fold(Self::ZERO, |acc, x| { 115 | acc * (Self::FIELD_SIZE_POWER_OF_TWO) + x 116 | }) // let acc = 117 | } 118 | 119 | /// Convert bytes into an Fp element, returning None if these bytes are not already reduced. 120 | /// 121 | /// This means that values that cannot be produced by encoding a field element will return 122 | /// None, enforcing canonical serialization. 123 | pub fn from_bytes_checked(bytes: &[u8; N_8]) -> Result { 124 | let reduced = Self::from_raw_bytes(bytes); 125 | if reduced.to_bytes_le() == *bytes { 126 | Ok(reduced) 127 | } else { 128 | Err(EncodingError::InvalidEncoding) 129 | } 130 | } 131 | 132 | pub fn to_bytes(&self) -> [u8; N_8] { 133 | self.to_bytes_le() 134 | } 135 | 136 | /// Sample a random field element uniformly. 137 | pub fn rand(rng: &mut R) -> Self { 138 | // Sample wide, reduce 139 | let bytes = { 140 | let mut out = [0u8; N_8 + 16]; 141 | rng.fill_bytes(&mut out); 142 | out 143 | }; 144 | Self::from_le_bytes_mod_order(&bytes) 145 | } 146 | } 147 | 148 | #[cfg(test)] 149 | mod test { 150 | use super::*; 151 | 152 | #[test] 153 | fn test_from_bytes_checked() { 154 | assert_eq!(Fp::from_bytes_checked(&[0; N_8]), Ok(Fp::ZERO)); 155 | assert!(Fp::from_bytes_checked(&[0xFF; N_8]).is_err()); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/fields/fp/ops.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cmp::Ordering, 3 | hash::Hash, 4 | iter::{Product, Sum}, 5 | ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, 6 | }; 7 | 8 | use crate::Fp; 9 | 10 | impl From for Fp { 11 | fn from(other: u128) -> Self { 12 | Self::from_le_limbs([other as u64, (other >> 64) as u64, 0, 0, 0, 0]) 13 | } 14 | } 15 | 16 | impl From for Fp { 17 | fn from(other: u64) -> Self { 18 | u128::from(other).into() 19 | } 20 | } 21 | 22 | impl From for Fp { 23 | fn from(other: u32) -> Self { 24 | u128::from(other).into() 25 | } 26 | } 27 | 28 | impl From for Fp { 29 | fn from(other: u16) -> Self { 30 | u128::from(other).into() 31 | } 32 | } 33 | 34 | impl From for Fp { 35 | fn from(other: u8) -> Self { 36 | u128::from(other).into() 37 | } 38 | } 39 | 40 | impl From for Fp { 41 | fn from(other: bool) -> Self { 42 | u128::from(other).into() 43 | } 44 | } 45 | 46 | impl Neg for Fp { 47 | type Output = Self; 48 | 49 | #[inline] 50 | #[must_use] 51 | fn neg(self) -> Self { 52 | let neg = self.neg(); 53 | neg 54 | } 55 | } 56 | 57 | impl<'a> AddAssign<&'a Self> for Fp { 58 | #[inline] 59 | fn add_assign(&mut self, other: &Self) { 60 | *self = self.add(other); 61 | } 62 | } 63 | 64 | impl AddAssign for Fp { 65 | #[inline(always)] 66 | fn add_assign(&mut self, other: Self) { 67 | *self = self.add(&other); 68 | } 69 | } 70 | 71 | impl<'a> AddAssign<&'a mut Self> for Fp { 72 | #[inline(always)] 73 | fn add_assign(&mut self, other: &'a mut Self) { 74 | *self = self.add(other); 75 | } 76 | } 77 | 78 | impl Add for Fp { 79 | type Output = Self; 80 | 81 | #[inline] 82 | fn add(self, other: Self) -> Self { 83 | self.add(&other) 84 | } 85 | } 86 | 87 | impl<'a> Add<&'a Fp> for Fp { 88 | type Output = Self; 89 | 90 | #[inline] 91 | fn add(self, other: &Self) -> Self { 92 | self.add(other) 93 | } 94 | } 95 | 96 | impl<'a> Add<&'a mut Self> for Fp { 97 | type Output = Self; 98 | 99 | #[inline] 100 | fn add(self, other: &'a mut Self) -> Self { 101 | self.add(other) 102 | } 103 | } 104 | 105 | impl<'a> SubAssign<&'a Self> for Fp { 106 | #[inline] 107 | fn sub_assign(&mut self, other: &Self) { 108 | *self = self.sub(other); 109 | } 110 | } 111 | 112 | impl SubAssign for Fp { 113 | #[inline(always)] 114 | fn sub_assign(&mut self, other: Self) { 115 | *self = self.sub(&other); 116 | } 117 | } 118 | 119 | impl<'a> SubAssign<&'a mut Self> for Fp { 120 | #[inline(always)] 121 | fn sub_assign(&mut self, other: &'a mut Self) { 122 | *self = self.sub(other); 123 | } 124 | } 125 | 126 | impl Sub for Fp { 127 | type Output = Self; 128 | 129 | #[inline] 130 | fn sub(self, other: Self) -> Self { 131 | self.sub(&other) 132 | } 133 | } 134 | 135 | impl<'a> Sub<&'a Fp> for Fp { 136 | type Output = Self; 137 | 138 | #[inline] 139 | fn sub(self, other: &Self) -> Self { 140 | self.sub(other) 141 | } 142 | } 143 | 144 | impl<'a> Sub<&'a mut Self> for Fp { 145 | type Output = Self; 146 | 147 | #[inline] 148 | fn sub(self, other: &'a mut Self) -> Self { 149 | self.sub(other) 150 | } 151 | } 152 | 153 | impl<'a> MulAssign<&'a Self> for Fp { 154 | fn mul_assign(&mut self, other: &Self) { 155 | *self = self.mul(other); 156 | } 157 | } 158 | 159 | impl core::ops::MulAssign for Fp { 160 | #[inline(always)] 161 | fn mul_assign(&mut self, other: Self) { 162 | *self = self.mul(&other); 163 | } 164 | } 165 | 166 | impl<'a> core::ops::MulAssign<&'a mut Self> for Fp { 167 | #[inline(always)] 168 | fn mul_assign(&mut self, other: &'a mut Self) { 169 | *self = self.mul(other); 170 | } 171 | } 172 | 173 | impl Mul for Fp { 174 | type Output = Self; 175 | 176 | #[inline(always)] 177 | fn mul(self, other: Self) -> Self { 178 | self.mul(&other) 179 | } 180 | } 181 | 182 | impl<'a> Mul<&'a Fp> for Fp { 183 | type Output = Self; 184 | 185 | #[inline] 186 | fn mul(self, other: &Self) -> Self { 187 | self.mul(other) 188 | } 189 | } 190 | 191 | impl<'a> Mul<&'a mut Self> for Fp { 192 | type Output = Self; 193 | 194 | #[inline(always)] 195 | fn mul(self, other: &'a mut Self) -> Self { 196 | self.mul(other) 197 | } 198 | } 199 | 200 | impl<'a> DivAssign<&'a Self> for Fp { 201 | #[inline(always)] 202 | fn div_assign(&mut self, other: &Self) { 203 | self.mul_assign(&other.inverse().unwrap()); 204 | } 205 | } 206 | 207 | impl DivAssign for Fp { 208 | #[inline(always)] 209 | fn div_assign(&mut self, other: Self) { 210 | self.div_assign(&other) 211 | } 212 | } 213 | 214 | impl<'a> DivAssign<&'a mut Self> for Fp { 215 | #[inline(always)] 216 | fn div_assign(&mut self, other: &'a mut Self) { 217 | self.div_assign(&*other) 218 | } 219 | } 220 | 221 | impl Div for Fp { 222 | type Output = Self; 223 | 224 | #[inline(always)] 225 | fn div(mut self, other: Self) -> Self { 226 | self.div_assign(&other); 227 | self 228 | } 229 | } 230 | 231 | impl<'a> Div<&'a Fp> for Fp { 232 | type Output = Self; 233 | 234 | #[inline] 235 | fn div(mut self, other: &Self) -> Self { 236 | self.mul_assign(&other.inverse().unwrap()); 237 | self 238 | } 239 | } 240 | 241 | impl<'a> Div<&'a mut Self> for Fp { 242 | type Output = Self; 243 | 244 | #[inline(always)] 245 | fn div(mut self, other: &'a mut Self) -> Self { 246 | self.div_assign(&*other); 247 | self 248 | } 249 | } 250 | 251 | impl Sum for Fp { 252 | fn sum>(iter: I) -> Self { 253 | iter.fold(Self::ZERO, Add::add) 254 | } 255 | } 256 | 257 | impl<'a> Sum<&'a Self> for Fp { 258 | fn sum>(iter: I) -> Self { 259 | iter.fold(Self::ZERO, Add::add) 260 | } 261 | } 262 | 263 | impl Product for Fp { 264 | fn product>(iter: I) -> Self { 265 | iter.fold(Self::ONE, Mul::mul) 266 | } 267 | } 268 | 269 | impl<'a> Product<&'a Self> for Fp { 270 | fn product>(iter: I) -> Self { 271 | iter.fold(Self::ONE, Mul::mul) 272 | } 273 | } 274 | 275 | impl Ord for Fp { 276 | #[inline(always)] 277 | fn cmp(&self, other: &Self) -> Ordering { 278 | let mut left = self.to_le_limbs(); 279 | let mut right = other.to_le_limbs(); 280 | left.reverse(); 281 | right.reverse(); 282 | left.cmp(&right) 283 | } 284 | } 285 | 286 | impl PartialOrd for Fp { 287 | #[inline(always)] 288 | fn partial_cmp(&self, other: &Self) -> Option { 289 | Some(self.cmp(other)) 290 | } 291 | } 292 | 293 | impl Hash for Fp { 294 | fn hash(&self, state: &mut H) { 295 | state.write(&self.to_bytes_le()) 296 | } 297 | } 298 | 299 | impl Default for Fp { 300 | fn default() -> Self { 301 | Self::ZERO 302 | } 303 | } 304 | 305 | impl core::fmt::Debug for Fp { 306 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 307 | let bytes = { 308 | let mut out = self.to_bytes_le(); 309 | out.reverse(); 310 | out 311 | }; 312 | let mut hex_chars = [0u8; 96]; 313 | hex::encode_to_slice(&bytes, &mut hex_chars) 314 | .expect("not enough space to write hex characters"); 315 | // Safety: hex characters will be valid UTF8. 316 | write!(f, "Fp(0x{})", unsafe { 317 | core::str::from_utf8_unchecked(&hex_chars) 318 | }) 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/fields/fp/u32.rs: -------------------------------------------------------------------------------- 1 | pub mod fiat; 2 | pub mod wrapper; 3 | 4 | pub use wrapper::Fp; 5 | -------------------------------------------------------------------------------- /src/fields/fp/u32/wrapper.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | super::{B, N_32, N_64, N_8}, 3 | fiat, 4 | }; 5 | 6 | const N: usize = N_32; 7 | 8 | #[derive(Copy, Clone)] 9 | pub struct Fp(fiat::FpMontgomeryDomainFieldElement); 10 | 11 | impl PartialEq for Fp { 12 | fn eq(&self, other: &Self) -> bool { 13 | let sub = self.sub(other); 14 | let mut check_word = 0; 15 | fiat::fp_nonzero(&mut check_word, &sub.0 .0); 16 | check_word == 0 17 | } 18 | } 19 | 20 | impl Eq for Fp {} 21 | 22 | impl zeroize::Zeroize for Fp { 23 | fn zeroize(&mut self) { 24 | self.0 .0.zeroize() 25 | } 26 | } 27 | 28 | impl Fp { 29 | pub(crate) fn from_le_limbs(limbs: [u64; N_64]) -> Fp { 30 | let limbs = { 31 | let mut out = [0u32; N]; 32 | for i in 0..N_64 { 33 | out[2 * i] = (limbs[i] & 0xFFFF_FFFF_FFFF_FFFF) as u32; 34 | out[2 * i + 1] = (limbs[i] >> 32) as u32; 35 | } 36 | out 37 | }; 38 | let x_non_monty = fiat::FpNonMontgomeryDomainFieldElement(limbs); 39 | let mut x = fiat::FpMontgomeryDomainFieldElement([0; N]); 40 | fiat::fp_to_montgomery(&mut x, &x_non_monty); 41 | Self(x) 42 | } 43 | 44 | pub(crate) fn from_raw_bytes(bytes: &[u8; N_8]) -> Fp { 45 | let mut x_non_montgomery = fiat::FpNonMontgomeryDomainFieldElement([0; N]); 46 | let mut x = fiat::FpMontgomeryDomainFieldElement([0; N]); 47 | 48 | fiat::fp_from_bytes(&mut x_non_montgomery.0, &bytes); 49 | fiat::fp_to_montgomery(&mut x, &x_non_montgomery); 50 | 51 | Self(x) 52 | } 53 | 54 | pub(crate) fn to_le_limbs(&self) -> [u64; N_64] { 55 | let mut x_non_montgomery = fiat::FpNonMontgomeryDomainFieldElement([0; N]); 56 | fiat::fp_from_montgomery(&mut x_non_montgomery, &self.0); 57 | let limbs = x_non_montgomery.0; 58 | let mut out = [0u64; N_64]; 59 | for i in 0..N_64 { 60 | out[i] = (limbs[2 * i] as u64) | ((limbs[2 * i + 1] as u64) << 32); 61 | } 62 | out 63 | } 64 | 65 | pub fn to_bytes_le(&self) -> [u8; N_8] { 66 | let mut bytes = [0u8; N_8]; 67 | let mut x_non_montgomery = fiat::FpNonMontgomeryDomainFieldElement([0; N]); 68 | fiat::fp_from_montgomery(&mut x_non_montgomery, &self.0); 69 | fiat::fp_to_bytes(&mut bytes, &x_non_montgomery.0); 70 | bytes 71 | } 72 | 73 | const fn from_montgomery_limbs_backend(limbs: [u32; N]) -> Fp { 74 | Self(fiat::FpMontgomeryDomainFieldElement(limbs)) 75 | } 76 | 77 | pub(crate) const fn from_montgomery_limbs(limbs: [u64; N_64]) -> Fp { 78 | Self(fiat::FpMontgomeryDomainFieldElement([ 79 | limbs[0] as u32, 80 | (limbs[0] >> 32) as u32, 81 | limbs[1] as u32, 82 | (limbs[1] >> 32) as u32, 83 | limbs[2] as u32, 84 | (limbs[2] >> 32) as u32, 85 | limbs[3] as u32, 86 | (limbs[3] >> 32) as u32, 87 | limbs[4] as u32, 88 | (limbs[4] >> 32) as u32, 89 | limbs[5] as u32, 90 | (limbs[5] >> 32) as u32, 91 | ])) 92 | } 93 | 94 | pub const ZERO: Self = Self(fiat::FpMontgomeryDomainFieldElement([0; N])); 95 | 96 | pub const ONE: Self = Self(fiat::FpMontgomeryDomainFieldElement([ 97 | 4294967144, 47054847, 2147483569, 1363189635, 2323464178, 2675815337, 1853645573, 98 | 2068748215, 2151449832, 1291097535, 3808294042, 9266785, 99 | ])); 100 | 101 | pub const QUADRATIC_NON_RESIDUE: Self = Self(fiat::FpMontgomeryDomainFieldElement([ 102 | 762, 4228612096, 3758096779, 2547227894, 3214954564, 544358927, 3648796674, 3418144080, 103 | 1477704171, 196026051, 3230948050, 10056866, 104 | ])); 105 | 106 | pub const MINUS_ONE: Self = Self(fiat::FpMontgomeryDomainFieldElement([ 107 | 153, 2184888320, 2952790095, 3318398400, 797706253, 2138418822, 2457383049, 2664710715, 108 | 3966026834, 2034659328, 885464144, 18928612, 109 | ])); 110 | 111 | pub fn square(&self) -> Fp { 112 | let mut result = fiat::FpMontgomeryDomainFieldElement([0; N]); 113 | fiat::fp_square(&mut result, &self.0); 114 | Self(result) 115 | } 116 | 117 | pub fn inverse(&self) -> Option { 118 | if self == &Self::ZERO { 119 | return None; 120 | } 121 | 122 | const I: usize = (49 * B + 57) / 17; 123 | 124 | let mut a = fiat::FpNonMontgomeryDomainFieldElement([0; N]); 125 | fiat::fp_from_montgomery(&mut a, &self.0); 126 | let mut d = 1; 127 | let mut f: [u32; N + 1] = [0u32; N + 1]; 128 | fiat::fp_msat(&mut f); 129 | let mut g: [u32; N + 1] = [0u32; N + 1]; 130 | let mut v: [u32; N] = [0u32; N]; 131 | let mut r: [u32; N] = Self::ONE.0 .0; 132 | let mut i = 0; 133 | let mut j = 0; 134 | 135 | while j < N { 136 | g[j] = a[j]; 137 | j += 1; 138 | } 139 | 140 | let mut out1: u32 = 0; 141 | let mut out2: [u32; N + 1] = [0; N + 1]; 142 | let mut out3: [u32; N + 1] = [0; N + 1]; 143 | let mut out4: [u32; N] = [0; N]; 144 | let mut out5: [u32; N] = [0; N]; 145 | let mut out6: u32 = 0; 146 | let mut out7: [u32; N + 1] = [0; N + 1]; 147 | let mut out8: [u32; N + 1] = [0; N + 1]; 148 | let mut out9: [u32; N] = [0; N]; 149 | let mut out10: [u32; N] = [0; N]; 150 | 151 | while i < I - I % 2 { 152 | fiat::fp_divstep( 153 | &mut out1, &mut out2, &mut out3, &mut out4, &mut out5, d, &f, &g, &v, &r, 154 | ); 155 | fiat::fp_divstep( 156 | &mut out6, &mut out7, &mut out8, &mut out9, &mut out10, out1, &out2, &out3, &out4, 157 | &out5, 158 | ); 159 | d = out6; 160 | f = out7; 161 | g = out8; 162 | v = out9; 163 | r = out10; 164 | i += 2; 165 | } 166 | 167 | if I % 2 != 0 { 168 | fiat::fp_divstep( 169 | &mut out1, &mut out2, &mut out3, &mut out4, &mut out5, d, &f, &g, &v, &r, 170 | ); 171 | v = out4; 172 | f = out2; 173 | } 174 | 175 | let s = ((f[f.len() - 1] >> (32 - 1)) & 1) as u8; 176 | let mut neg = fiat::FpMontgomeryDomainFieldElement([0; N]); 177 | fiat::fp_opp(&mut neg, &fiat::FpMontgomeryDomainFieldElement(v)); 178 | 179 | let mut v_prime: [u32; N] = [0u32; N]; 180 | fiat::fp_selectznz(&mut v_prime, s, &v, &neg.0); 181 | 182 | let mut pre_comp: [u32; N] = [0u32; N]; 183 | fiat::fp_divstep_precomp(&mut pre_comp); 184 | 185 | let mut result = fiat::FpMontgomeryDomainFieldElement([0; N]); 186 | fiat::fp_mul( 187 | &mut result, 188 | &fiat::FpMontgomeryDomainFieldElement(v_prime), 189 | &fiat::FpMontgomeryDomainFieldElement(pre_comp), 190 | ); 191 | 192 | Some(Fp(result)) 193 | } 194 | 195 | pub fn add(self, other: &Fp) -> Fp { 196 | let mut result = fiat::FpMontgomeryDomainFieldElement([0; N]); 197 | fiat::fp_add(&mut result, &self.0, &other.0); 198 | Fp(result) 199 | } 200 | 201 | pub fn sub(self, other: &Fp) -> Fp { 202 | let mut result = fiat::FpMontgomeryDomainFieldElement([0; N]); 203 | fiat::fp_sub(&mut result, &self.0, &other.0); 204 | Fp(result) 205 | } 206 | 207 | pub fn mul(self, other: &Fp) -> Fp { 208 | let mut result = fiat::FpMontgomeryDomainFieldElement([0; N]); 209 | fiat::fp_mul(&mut result, &self.0, &other.0); 210 | Fp(result) 211 | } 212 | 213 | pub fn neg(self) -> Fp { 214 | let mut result = fiat::FpMontgomeryDomainFieldElement([0; N]); 215 | fiat::fp_opp(&mut result, &self.0); 216 | Fp(result) 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/fields/fp/u64.rs: -------------------------------------------------------------------------------- 1 | pub mod wrapper; 2 | 3 | pub use wrapper::Fp; 4 | -------------------------------------------------------------------------------- /src/fields/fp/u64/wrapper.rs: -------------------------------------------------------------------------------- 1 | use ark_bls12_377::Fq as ArkworksFp; 2 | use ark_ff::{BigInt, Field, PrimeField}; 3 | use ark_serialize::CanonicalSerialize; 4 | 5 | use super::super::{N_64, N_8}; 6 | 7 | const N: usize = N_64; 8 | 9 | #[derive(Copy, Clone)] 10 | pub struct Fp(ArkworksFp); 11 | 12 | impl PartialEq for Fp { 13 | fn eq(&self, other: &Self) -> bool { 14 | self.0 == other.0 15 | } 16 | } 17 | 18 | impl Eq for Fp {} 19 | 20 | impl zeroize::Zeroize for Fp { 21 | fn zeroize(&mut self) { 22 | self.0 .0.zeroize() 23 | } 24 | } 25 | 26 | impl Fp { 27 | pub(crate) fn from_le_limbs(limbs: [u64; N_64]) -> Fp { 28 | let mut bytes = [0u8; N_8]; 29 | for i in 0..N_64 { 30 | let this_byte = limbs[i].to_le_bytes(); 31 | for j in 0..8 { 32 | bytes[8 * i + j] = this_byte[j]; 33 | } 34 | } 35 | 36 | Self::from_raw_bytes(&bytes) 37 | } 38 | 39 | pub(crate) fn from_raw_bytes(bytes: &[u8; N_8]) -> Fp { 40 | Self(ArkworksFp::from_le_bytes_mod_order(bytes)) 41 | } 42 | 43 | pub(crate) fn to_le_limbs(&self) -> [u64; N_64] { 44 | let le_bytes = self.to_bytes_le(); 45 | let mut out = [0u64; N_64]; 46 | for i in 0..N_64 { 47 | out[i] = u64::from_le_bytes([ 48 | le_bytes[8 * i], 49 | le_bytes[8 * i + 1], 50 | le_bytes[8 * i + 2], 51 | le_bytes[8 * i + 3], 52 | le_bytes[8 * i + 4], 53 | le_bytes[8 * i + 5], 54 | le_bytes[8 * i + 6], 55 | le_bytes[8 * i + 7], 56 | ]); 57 | } 58 | out 59 | } 60 | 61 | pub fn to_bytes_le(&self) -> [u8; N_8] { 62 | let mut bytes = [0u8; 48]; 63 | self.0 64 | .serialize_compressed(&mut bytes[..]) 65 | .expect("serialization into array should be infallible"); 66 | bytes 67 | } 68 | 69 | pub(crate) const fn from_montgomery_limbs(limbs: [u64; N]) -> Fp { 70 | Self(ArkworksFp::new_unchecked(BigInt(limbs))) 71 | } 72 | 73 | pub const ZERO: Self = Self(ArkworksFp::new(BigInt::new([0; N]))); 74 | pub const ONE: Self = Self(ArkworksFp::new(BigInt::one())); 75 | 76 | pub const MINUS_ONE: Self = Self::from_montgomery_limbs([ 77 | 9384023879812382873, 78 | 14252412606051516495, 79 | 9184438906438551565, 80 | 11444845376683159689, 81 | 8738795276227363922, 82 | 81297770384137296, 83 | ]); 84 | 85 | pub const QUADRATIC_NON_RESIDUE: Self = Self::from_montgomery_limbs([ 86 | 18161750659790013178, 87 | 10940260503947051403, 88 | 2338003791965605956, 89 | 14680817040264804354, 90 | 841925479686732267, 91 | 43193913801202386, 92 | ]); 93 | 94 | pub fn square(&self) -> Fp { 95 | Fp(self.0.square()) 96 | } 97 | 98 | pub fn inverse(&self) -> Option { 99 | if self == &Self::ZERO { 100 | return None; 101 | } 102 | 103 | Some(Fp(self.0.inverse()?)) 104 | } 105 | 106 | pub fn add(self, other: &Fp) -> Fp { 107 | Fp(self.0 + other.0) 108 | } 109 | 110 | pub fn sub(self, other: &Fp) -> Fp { 111 | Fp(self.0 - other.0) 112 | } 113 | 114 | pub fn mul(self, other: &Fp) -> Fp { 115 | Fp(self.0 * other.0) 116 | } 117 | 118 | pub fn neg(self) -> Fp { 119 | Fp(-self.0) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/fields/fq.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | use rand_core::CryptoRngCore; 3 | 4 | use crate::EncodingError; 5 | 6 | #[cfg(feature = "arkworks")] 7 | pub mod arkworks; 8 | mod ops; 9 | mod u32; 10 | 11 | // The u64 backend requires arkworks 12 | #[cfg(feature = "arkworks")] 13 | mod u64; 14 | 15 | cfg_if! { 16 | if #[cfg(feature = "arkworks")] { 17 | pub type Fq = u64::Fq; 18 | } else { 19 | pub type Fq = u32::Fq; 20 | } 21 | } 22 | 23 | const B: usize = 253; 24 | const N_8: usize = (B + 7) / 8; 25 | const N_32: usize = (B + 31) / 32; 26 | const N_64: usize = (B + 63) / 64; 27 | 28 | impl Fq { 29 | pub const MODULUS_LIMBS: [u64; N_64] = [ 30 | 725501752471715841, 31 | 6461107452199829505, 32 | 6968279316240510977, 33 | 1345280370688173398, 34 | ]; 35 | 36 | pub const MODULUS_MINUS_ONE_DIV_TWO_LIMBS: [u64; N_64] = [ 37 | 9586122913090633728, 38 | 12453925762954690560, 39 | 3484139658120255488, 40 | 672640185344086699, 41 | ]; 42 | 43 | pub const MODULUS_BIT_SIZE: u32 = 0xfd; 44 | 45 | pub const TRACE_LIMBS: [u64; N_64] = [ 46 | 17149038877957297187, 47 | 11113960768935211860, 48 | 14608890324369326440, 49 | 9558, 50 | ]; 51 | 52 | pub const TRACE_MINUS_ONE_DIV_TWO_LIMBS: [u64; N_64] = [ 53 | 8574519438978648593, 54 | 5556980384467605930, 55 | 7304445162184663220, 56 | 4779, 57 | ]; 58 | 59 | // c1 60 | pub const TWO_ADICITY: u32 = 0x2f; 61 | 62 | pub const QUADRATIC_NON_RESIDUE_TO_TRACE: Self = Self::from_montgomery_limbs([ 63 | 4340692304772210610, 64 | 11102725085307959083, 65 | 15540458298643990566, 66 | 944526744080888988, 67 | ]); 68 | 69 | pub const MULTIPLICATIVE_GENERATOR: Self = Self::from_montgomery_limbs([ 70 | 2984901390528151251, 71 | 10561528701063790279, 72 | 5476750214495080041, 73 | 898978044469942640, 74 | ]); 75 | 76 | pub const TWO_ADIC_ROOT_OF_UNITY: Self = Self::from_montgomery_limbs([ 77 | 12646347781564978760, 78 | 6783048705277173164, 79 | 268534165941069093, 80 | 1121515446318641358, 81 | ]); 82 | 83 | pub const FIELD_SIZE_POWER_OF_TWO: Self = Self::from_montgomery_limbs([ 84 | 2726216793283724667, 85 | 14712177743343147295, 86 | 12091039717619697043, 87 | 81024008013859129, 88 | ]); 89 | 90 | pub fn from_le_bytes_mod_order(bytes: &[u8]) -> Self { 91 | bytes 92 | .chunks(N_8) 93 | .map(|x| { 94 | let mut padded = [0u8; N_8]; 95 | padded[..x.len()].copy_from_slice(x); 96 | Self::from_raw_bytes(&padded) 97 | }) // [X, 2^(256) * X, ...] 98 | .rev() 99 | .fold(Self::ZERO, |acc, x| { 100 | acc * (Self::FIELD_SIZE_POWER_OF_TWO) + x 101 | }) // let acc = 102 | } 103 | 104 | /// Convert bytes into an Fq element, returning None if these bytes are not already reduced. 105 | /// 106 | /// This means that values that cannot be produced by encoding a field element will return 107 | /// None, enforcing canonical serialization. 108 | pub fn from_bytes_checked(bytes: &[u8; N_8]) -> Result { 109 | let reduced = Self::from_raw_bytes(bytes); 110 | if reduced.to_bytes_le() == *bytes { 111 | Ok(reduced) 112 | } else { 113 | Err(EncodingError::InvalidEncoding) 114 | } 115 | } 116 | 117 | pub fn to_bytes(&self) -> [u8; N_8] { 118 | self.to_bytes_le() 119 | } 120 | 121 | /// Sample a random field element uniformly. 122 | pub fn rand(rng: &mut R) -> Self { 123 | // Sample wide, reduce 124 | let bytes = { 125 | let mut out = [0u8; N_8 + 16]; 126 | rng.fill_bytes(&mut out); 127 | out 128 | }; 129 | Self::from_le_bytes_mod_order(&bytes) 130 | } 131 | 132 | /// Raise this element to a given power. 133 | /// 134 | /// Note: Arkworks provides another method for this, called `pow`. 135 | pub fn power>(&self, exp: S) -> Self { 136 | let mut res = Fq::from(1u64); 137 | let exp_u64 = exp.as_ref(); 138 | for _ in 0..exp_u64[0] { 139 | res *= self; 140 | } 141 | res 142 | } 143 | } 144 | 145 | #[cfg(test)] 146 | mod test { 147 | use super::*; 148 | 149 | #[test] 150 | fn test_from_bytes_checked() { 151 | assert_eq!(Fq::from_bytes_checked(&[0; N_8]), Ok(Fq::ZERO)); 152 | assert!(Fq::from_bytes_checked(&[0xFF; N_8]).is_err()); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/fields/fq/ops.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cmp::Ordering, 3 | hash::Hash, 4 | iter::{Product, Sum}, 5 | ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, 6 | }; 7 | 8 | use crate::Fq; 9 | 10 | impl From for Fq { 11 | fn from(other: u128) -> Self { 12 | Self::from_le_limbs([other as u64, (other >> 64) as u64, 0, 0]) 13 | } 14 | } 15 | 16 | impl From for Fq { 17 | fn from(other: u64) -> Self { 18 | u128::from(other).into() 19 | } 20 | } 21 | 22 | impl From for Fq { 23 | fn from(other: u32) -> Self { 24 | u128::from(other).into() 25 | } 26 | } 27 | 28 | impl From for Fq { 29 | fn from(other: u16) -> Self { 30 | u128::from(other).into() 31 | } 32 | } 33 | 34 | impl From for Fq { 35 | fn from(other: u8) -> Self { 36 | u128::from(other).into() 37 | } 38 | } 39 | 40 | impl From for Fq { 41 | fn from(other: bool) -> Self { 42 | u128::from(other).into() 43 | } 44 | } 45 | 46 | impl Neg for Fq { 47 | type Output = Self; 48 | 49 | #[inline] 50 | #[must_use] 51 | fn neg(self) -> Self { 52 | let neg = self.neg(); 53 | neg 54 | } 55 | } 56 | 57 | impl<'a> AddAssign<&'a Self> for Fq { 58 | #[inline] 59 | fn add_assign(&mut self, other: &Self) { 60 | *self = self.add(other); 61 | } 62 | } 63 | 64 | impl AddAssign for Fq { 65 | #[inline(always)] 66 | fn add_assign(&mut self, other: Self) { 67 | *self = self.add(&other); 68 | } 69 | } 70 | 71 | impl<'a> AddAssign<&'a mut Self> for Fq { 72 | #[inline(always)] 73 | fn add_assign(&mut self, other: &'a mut Self) { 74 | *self = self.add(other); 75 | } 76 | } 77 | 78 | impl Add for Fq { 79 | type Output = Self; 80 | 81 | #[inline] 82 | fn add(self, other: Self) -> Self { 83 | self.add(&other) 84 | } 85 | } 86 | 87 | impl<'a> Add<&'a Fq> for Fq { 88 | type Output = Self; 89 | 90 | #[inline] 91 | fn add(self, other: &Self) -> Self { 92 | self.add(other) 93 | } 94 | } 95 | 96 | impl<'a> Add<&'a mut Self> for Fq { 97 | type Output = Self; 98 | 99 | #[inline] 100 | fn add(self, other: &'a mut Self) -> Self { 101 | self.add(other) 102 | } 103 | } 104 | 105 | impl<'a> SubAssign<&'a Self> for Fq { 106 | #[inline] 107 | fn sub_assign(&mut self, other: &Self) { 108 | *self = self.sub(other); 109 | } 110 | } 111 | 112 | impl SubAssign for Fq { 113 | #[inline(always)] 114 | fn sub_assign(&mut self, other: Self) { 115 | *self = self.sub(&other); 116 | } 117 | } 118 | 119 | impl<'a> SubAssign<&'a mut Self> for Fq { 120 | #[inline(always)] 121 | fn sub_assign(&mut self, other: &'a mut Self) { 122 | *self = self.sub(other); 123 | } 124 | } 125 | 126 | impl Sub for Fq { 127 | type Output = Self; 128 | 129 | #[inline] 130 | fn sub(self, other: Self) -> Self { 131 | self.sub(&other) 132 | } 133 | } 134 | 135 | impl<'a> Sub<&'a Fq> for Fq { 136 | type Output = Self; 137 | 138 | #[inline] 139 | fn sub(self, other: &Self) -> Self { 140 | self.sub(other) 141 | } 142 | } 143 | 144 | impl<'a> Sub<&'a mut Self> for Fq { 145 | type Output = Self; 146 | 147 | #[inline] 148 | fn sub(self, other: &'a mut Self) -> Self { 149 | self.sub(other) 150 | } 151 | } 152 | 153 | impl<'a> MulAssign<&'a Self> for Fq { 154 | fn mul_assign(&mut self, other: &Self) { 155 | *self = self.mul(other); 156 | } 157 | } 158 | 159 | impl core::ops::MulAssign for Fq { 160 | #[inline(always)] 161 | fn mul_assign(&mut self, other: Self) { 162 | *self = self.mul(&other); 163 | } 164 | } 165 | 166 | impl<'a> core::ops::MulAssign<&'a mut Self> for Fq { 167 | #[inline(always)] 168 | fn mul_assign(&mut self, other: &'a mut Self) { 169 | *self = self.mul(other); 170 | } 171 | } 172 | 173 | impl Mul for Fq { 174 | type Output = Self; 175 | 176 | #[inline(always)] 177 | fn mul(self, other: Self) -> Self { 178 | self.mul(&other) 179 | } 180 | } 181 | 182 | impl<'a> Mul<&'a Fq> for Fq { 183 | type Output = Self; 184 | 185 | #[inline] 186 | fn mul(self, other: &Self) -> Self { 187 | self.mul(other) 188 | } 189 | } 190 | 191 | impl<'a> Mul<&'a mut Self> for Fq { 192 | type Output = Self; 193 | 194 | #[inline(always)] 195 | fn mul(self, other: &'a mut Self) -> Self { 196 | self.mul(other) 197 | } 198 | } 199 | 200 | impl<'a> DivAssign<&'a Self> for Fq { 201 | #[inline(always)] 202 | fn div_assign(&mut self, other: &Self) { 203 | self.mul_assign(&other.inverse().unwrap()); 204 | } 205 | } 206 | 207 | impl DivAssign for Fq { 208 | #[inline(always)] 209 | fn div_assign(&mut self, other: Self) { 210 | self.div_assign(&other) 211 | } 212 | } 213 | 214 | impl<'a> DivAssign<&'a mut Self> for Fq { 215 | #[inline(always)] 216 | fn div_assign(&mut self, other: &'a mut Self) { 217 | self.div_assign(&*other) 218 | } 219 | } 220 | 221 | impl Div for Fq { 222 | type Output = Self; 223 | 224 | #[inline(always)] 225 | fn div(mut self, other: Self) -> Self { 226 | self.div_assign(&other); 227 | self 228 | } 229 | } 230 | 231 | impl<'a> Div<&'a Fq> for Fq { 232 | type Output = Self; 233 | 234 | #[inline] 235 | fn div(mut self, other: &Self) -> Self { 236 | self.mul_assign(&other.inverse().unwrap()); 237 | self 238 | } 239 | } 240 | 241 | impl<'a> Div<&'a mut Self> for Fq { 242 | type Output = Self; 243 | 244 | #[inline(always)] 245 | fn div(mut self, other: &'a mut Self) -> Self { 246 | self.div_assign(&*other); 247 | self 248 | } 249 | } 250 | 251 | impl Sum for Fq { 252 | fn sum>(iter: I) -> Self { 253 | iter.fold(Self::ZERO, Add::add) 254 | } 255 | } 256 | 257 | impl<'a> Sum<&'a Self> for Fq { 258 | fn sum>(iter: I) -> Self { 259 | iter.fold(Self::ZERO, Add::add) 260 | } 261 | } 262 | 263 | impl Product for Fq { 264 | fn product>(iter: I) -> Self { 265 | iter.fold(Self::ONE, Mul::mul) 266 | } 267 | } 268 | 269 | impl<'a> Product<&'a Self> for Fq { 270 | fn product>(iter: I) -> Self { 271 | iter.fold(Self::ONE, Mul::mul) 272 | } 273 | } 274 | 275 | impl Ord for Fq { 276 | #[inline(always)] 277 | fn cmp(&self, other: &Self) -> Ordering { 278 | let mut left = self.to_le_limbs(); 279 | let mut right = other.to_le_limbs(); 280 | left.reverse(); 281 | right.reverse(); 282 | left.cmp(&right) 283 | } 284 | } 285 | 286 | impl PartialOrd for Fq { 287 | #[inline(always)] 288 | fn partial_cmp(&self, other: &Self) -> Option { 289 | Some(self.cmp(other)) 290 | } 291 | } 292 | 293 | impl Hash for Fq { 294 | fn hash(&self, state: &mut H) { 295 | state.write(&self.to_bytes_le()) 296 | } 297 | } 298 | 299 | impl Default for Fq { 300 | fn default() -> Self { 301 | Self::ZERO 302 | } 303 | } 304 | 305 | impl core::fmt::Debug for Fq { 306 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 307 | let bytes = { 308 | let mut out = self.to_bytes_le(); 309 | out.reverse(); 310 | out 311 | }; 312 | let mut hex_chars = [0u8; 64]; 313 | hex::encode_to_slice(&bytes, &mut hex_chars) 314 | .expect("not enough space to write hex characters"); 315 | // Safety: hex characters will be valid UTF8. 316 | write!(f, "Fq(0x{})", unsafe { 317 | core::str::from_utf8_unchecked(&hex_chars) 318 | }) 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/fields/fq/u32.rs: -------------------------------------------------------------------------------- 1 | pub mod fiat; 2 | pub mod wrapper; 3 | 4 | pub use wrapper::Fq; 5 | -------------------------------------------------------------------------------- /src/fields/fq/u32/wrapper.rs: -------------------------------------------------------------------------------- 1 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; 2 | 3 | use super::{ 4 | super::{B, N_32, N_64, N_8}, 5 | fiat, 6 | }; 7 | 8 | const N: usize = N_32; 9 | 10 | #[derive(Copy, Clone)] 11 | pub struct Fq(fiat::FqMontgomeryDomainFieldElement); 12 | 13 | impl PartialEq for Fq { 14 | fn eq(&self, other: &Self) -> bool { 15 | match (self.is_sentinel(), other.is_sentinel()) { 16 | (true, true) => true, 17 | (true, false) => false, 18 | (false, true) => false, 19 | (false, false) => { 20 | let sub = self.sub(other); 21 | let mut check_word = 0; 22 | fiat::fq_nonzero(&mut check_word, &sub.0 .0); 23 | check_word == 0 24 | } 25 | } 26 | } 27 | } 28 | 29 | impl Eq for Fq {} 30 | 31 | impl zeroize::Zeroize for Fq { 32 | fn zeroize(&mut self) { 33 | self.0 .0.zeroize() 34 | } 35 | } 36 | 37 | impl Fq { 38 | pub(crate) fn from_le_limbs(limbs: [u64; N_64]) -> Fq { 39 | let limbs = { 40 | let mut out = [0u32; N]; 41 | for i in 0..N_64 { 42 | out[2 * i] = (limbs[i] & 0xFFFF_FFFF_FFFF_FFFF) as u32; 43 | out[2 * i + 1] = (limbs[i] >> 32) as u32; 44 | } 45 | out 46 | }; 47 | let x_non_monty = fiat::FqNonMontgomeryDomainFieldElement(limbs); 48 | let mut x = fiat::FqMontgomeryDomainFieldElement([0; N]); 49 | fiat::fq_to_montgomery(&mut x, &x_non_monty); 50 | Self(x) 51 | } 52 | 53 | pub(crate) fn from_raw_bytes(bytes: &[u8; N_8]) -> Fq { 54 | let mut x_non_montgomery = fiat::FqNonMontgomeryDomainFieldElement([0; N]); 55 | let mut x = fiat::FqMontgomeryDomainFieldElement([0; N]); 56 | 57 | fiat::fq_from_bytes(&mut x_non_montgomery.0, &bytes); 58 | fiat::fq_to_montgomery(&mut x, &x_non_montgomery); 59 | 60 | Self(x) 61 | } 62 | 63 | pub(crate) fn to_le_limbs(&self) -> [u64; N_64] { 64 | debug_assert!(!self.is_sentinel()); 65 | 66 | let mut x_non_montgomery = fiat::FqNonMontgomeryDomainFieldElement([0; N]); 67 | fiat::fq_from_montgomery(&mut x_non_montgomery, &self.0); 68 | let limbs = x_non_montgomery.0; 69 | let mut out = [0u64; N_64]; 70 | for i in 0..N_64 { 71 | out[i] = (limbs[2 * i] as u64) | ((limbs[2 * i + 1] as u64) << 32); 72 | } 73 | out 74 | } 75 | 76 | pub fn to_bytes_le(&self) -> [u8; N_8] { 77 | debug_assert!(!self.is_sentinel()); 78 | 79 | let mut bytes = [0u8; N_8]; 80 | let mut x_non_montgomery = fiat::FqNonMontgomeryDomainFieldElement([0; N]); 81 | fiat::fq_from_montgomery(&mut x_non_montgomery, &self.0); 82 | fiat::fq_to_bytes(&mut bytes, &x_non_montgomery.0); 83 | bytes 84 | } 85 | 86 | const fn from_montgomery_limbs_backend(limbs: [u32; N]) -> Fq { 87 | Self(fiat::FqMontgomeryDomainFieldElement(limbs)) 88 | } 89 | 90 | /// Instantiate a constant field element from its montgomery limbs. 91 | /// 92 | /// This should only be used if you are familiar with the internals of the library. 93 | pub const fn from_montgomery_limbs(limbs: [u64; N_64]) -> Fq { 94 | Self(fiat::FqMontgomeryDomainFieldElement([ 95 | limbs[0] as u32, 96 | (limbs[0] >> 32) as u32, 97 | limbs[1] as u32, 98 | (limbs[1] >> 32) as u32, 99 | limbs[2] as u32, 100 | (limbs[2] >> 32) as u32, 101 | limbs[3] as u32, 102 | (limbs[3] >> 32) as u32, 103 | ])) 104 | } 105 | 106 | pub const ZERO: Self = Self(fiat::FqMontgomeryDomainFieldElement([0; N])); 107 | 108 | pub const ONE: Self = Self(fiat::FqMontgomeryDomainFieldElement([ 109 | 4294967283, 2099019775, 1879048178, 1918366991, 1361842158, 383260021, 733715101, 223074866, 110 | ])); 111 | /// 112 | /// A sentinel value which exists only to not be equal to any other field element. 113 | /// 114 | /// Operations involving this element are undefined. 115 | pub const SENTINEL: Self = Self::from_montgomery_limbs([u64::MAX; N_64]); 116 | 117 | fn is_sentinel(&self) -> bool { 118 | self.0 .0 == Self::SENTINEL.0 .0 119 | } 120 | 121 | pub fn square(&self) -> Fq { 122 | debug_assert!(!self.is_sentinel()); 123 | 124 | let mut result = fiat::FqMontgomeryDomainFieldElement([0; N]); 125 | fiat::fq_square(&mut result, &self.0); 126 | Self(result) 127 | } 128 | 129 | pub fn inverse(&self) -> Option { 130 | debug_assert!(!self.is_sentinel()); 131 | 132 | if self == &Self::ZERO { 133 | return None; 134 | } 135 | 136 | const I: usize = (49 * B + 57) / 17; 137 | 138 | let mut a = fiat::FqNonMontgomeryDomainFieldElement([0; N]); 139 | fiat::fq_from_montgomery(&mut a, &self.0); 140 | let mut d = 1; 141 | let mut f: [u32; N + 1] = [0u32; N + 1]; 142 | fiat::fq_msat(&mut f); 143 | let mut g: [u32; N + 1] = [0u32; N + 1]; 144 | let mut v: [u32; N] = [0u32; N]; 145 | let mut r: [u32; N] = Self::ONE.0 .0; 146 | let mut i = 0; 147 | let mut j = 0; 148 | 149 | while j < N { 150 | g[j] = a[j]; 151 | j += 1; 152 | } 153 | 154 | let mut out1: u32 = 0; 155 | let mut out2: [u32; N + 1] = [0; N + 1]; 156 | let mut out3: [u32; N + 1] = [0; N + 1]; 157 | let mut out4: [u32; N] = [0; N]; 158 | let mut out5: [u32; N] = [0; N]; 159 | let mut out6: u32 = 0; 160 | let mut out7: [u32; N + 1] = [0; N + 1]; 161 | let mut out8: [u32; N + 1] = [0; N + 1]; 162 | let mut out9: [u32; N] = [0; N]; 163 | let mut out10: [u32; N] = [0; N]; 164 | 165 | while i < I - I % 2 { 166 | fiat::fq_divstep( 167 | &mut out1, &mut out2, &mut out3, &mut out4, &mut out5, d, &f, &g, &v, &r, 168 | ); 169 | fiat::fq_divstep( 170 | &mut out6, &mut out7, &mut out8, &mut out9, &mut out10, out1, &out2, &out3, &out4, 171 | &out5, 172 | ); 173 | d = out6; 174 | f = out7; 175 | g = out8; 176 | v = out9; 177 | r = out10; 178 | i += 2; 179 | } 180 | 181 | if I % 2 != 0 { 182 | fiat::fq_divstep( 183 | &mut out1, &mut out2, &mut out3, &mut out4, &mut out5, d, &f, &g, &v, &r, 184 | ); 185 | v = out4; 186 | f = out2; 187 | } 188 | 189 | let s = ((f[f.len() - 1] >> (32 - 1)) & 1) as u8; 190 | let mut neg = fiat::FqMontgomeryDomainFieldElement([0; N]); 191 | fiat::fq_opp(&mut neg, &fiat::FqMontgomeryDomainFieldElement(v)); 192 | 193 | let mut v_prime: [u32; N] = [0u32; N]; 194 | fiat::fq_selectznz(&mut v_prime, s, &v, &neg.0); 195 | 196 | let mut pre_comp: [u32; N] = [0u32; N]; 197 | fiat::fq_divstep_precomp(&mut pre_comp); 198 | 199 | let mut result = fiat::FqMontgomeryDomainFieldElement([0; N]); 200 | fiat::fq_mul( 201 | &mut result, 202 | &fiat::FqMontgomeryDomainFieldElement(v_prime), 203 | &fiat::FqMontgomeryDomainFieldElement(pre_comp), 204 | ); 205 | 206 | Some(Fq(result)) 207 | } 208 | 209 | pub fn add(self, other: &Fq) -> Fq { 210 | debug_assert!(!self.is_sentinel() && !other.is_sentinel()); 211 | 212 | let mut result = fiat::FqMontgomeryDomainFieldElement([0; N]); 213 | fiat::fq_add(&mut result, &self.0, &other.0); 214 | Fq(result) 215 | } 216 | 217 | pub fn sub(self, other: &Fq) -> Fq { 218 | debug_assert!(!self.is_sentinel() && !other.is_sentinel()); 219 | 220 | let mut result = fiat::FqMontgomeryDomainFieldElement([0; N]); 221 | fiat::fq_sub(&mut result, &self.0, &other.0); 222 | Fq(result) 223 | } 224 | 225 | pub fn mul(self, other: &Fq) -> Fq { 226 | debug_assert!(!self.is_sentinel() && !other.is_sentinel()); 227 | 228 | let mut result = fiat::FqMontgomeryDomainFieldElement([0; N]); 229 | fiat::fq_mul(&mut result, &self.0, &other.0); 230 | Fq(result) 231 | } 232 | 233 | pub fn neg(self) -> Fq { 234 | debug_assert!(!self.is_sentinel()); 235 | 236 | let mut result = fiat::FqMontgomeryDomainFieldElement([0; N]); 237 | fiat::fq_opp(&mut result, &self.0); 238 | Fq(result) 239 | } 240 | } 241 | 242 | impl ConditionallySelectable for Fq { 243 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 244 | let mut out = [0u32; 8]; 245 | for i in 0..8 { 246 | out[i] = u32::conditional_select(&a.0 .0[i], &b.0 .0[i], choice); 247 | } 248 | Self(fiat::FqMontgomeryDomainFieldElement(out)) 249 | } 250 | } 251 | 252 | impl ConstantTimeEq for Fq { 253 | fn ct_eq(&self, other: &Fq) -> Choice { 254 | self.0 .0.ct_eq(&other.0 .0) 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /src/fields/fq/u64.rs: -------------------------------------------------------------------------------- 1 | pub mod wrapper; 2 | 3 | pub use wrapper::Fq; 4 | -------------------------------------------------------------------------------- /src/fields/fq/u64/wrapper.rs: -------------------------------------------------------------------------------- 1 | use ark_ed_on_bls12_377::Fq as ArkworksFq; 2 | use ark_ff::{biginteger::BigInt, Field, PrimeField}; 3 | use ark_serialize::CanonicalSerialize; 4 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; 5 | 6 | use super::super::{N_64, N_8}; 7 | 8 | const N: usize = N_64; 9 | 10 | #[derive(Copy, Clone)] 11 | pub struct Fq(ArkworksFq); 12 | 13 | impl PartialEq for Fq { 14 | fn eq(&self, other: &Self) -> bool { 15 | match (self.is_sentinel(), other.is_sentinel()) { 16 | (true, true) => true, 17 | (true, false) => false, 18 | (false, true) => false, 19 | (false, false) => self.0 == other.0, 20 | } 21 | } 22 | } 23 | 24 | impl Eq for Fq {} 25 | 26 | impl zeroize::Zeroize for Fq { 27 | fn zeroize(&mut self) { 28 | self.0 .0.zeroize() 29 | } 30 | } 31 | 32 | impl Fq { 33 | pub(crate) fn from_le_limbs(limbs: [u64; N_64]) -> Fq { 34 | let mut bytes = [0u8; N_8]; 35 | for i in 0..N_64 { 36 | let this_byte = limbs[i].to_le_bytes(); 37 | for j in 0..8 { 38 | bytes[8 * i + j] = this_byte[j]; 39 | } 40 | } 41 | 42 | Self::from_raw_bytes(&bytes) 43 | } 44 | 45 | pub(crate) fn from_raw_bytes(bytes: &[u8; N_8]) -> Fq { 46 | Self(ArkworksFq::from_le_bytes_mod_order(bytes)) 47 | } 48 | 49 | pub(crate) fn to_le_limbs(&self) -> [u64; N_64] { 50 | debug_assert!(!self.is_sentinel()); 51 | 52 | let le_bytes = self.to_bytes_le(); 53 | let mut out = [0u64; N_64]; 54 | for i in 0..N_64 { 55 | out[i] = u64::from_le_bytes([ 56 | le_bytes[8 * i], 57 | le_bytes[8 * i + 1], 58 | le_bytes[8 * i + 2], 59 | le_bytes[8 * i + 3], 60 | le_bytes[8 * i + 4], 61 | le_bytes[8 * i + 5], 62 | le_bytes[8 * i + 6], 63 | le_bytes[8 * i + 7], 64 | ]); 65 | } 66 | out 67 | } 68 | 69 | pub fn to_bytes_le(&self) -> [u8; N_8] { 70 | debug_assert!(!self.is_sentinel()); 71 | 72 | let mut bytes = [0u8; 32]; 73 | self.0 74 | .serialize_compressed(&mut bytes[..]) 75 | .expect("serialization into array should be infallible"); 76 | bytes 77 | } 78 | 79 | /// Instantiate a constant field element from its montgomery limbs. 80 | /// 81 | /// This should only be used if you are familiar with the internals of the library. 82 | pub const fn from_montgomery_limbs(limbs: [u64; N]) -> Fq { 83 | // The Arkworks `Fp::new_unchecked` method does not perform montgomery reduction. 84 | Self(ArkworksFq::new_unchecked(BigInt::new(limbs))) 85 | } 86 | 87 | pub const ZERO: Self = Self(ArkworksFq::new(BigInt::new([0; N]))); 88 | pub const ONE: Self = Self(ArkworksFq::new(BigInt::one())); 89 | 90 | /// A sentinel value which exists only to not be equal to any other field element. 91 | /// 92 | /// Operations involving this element are undefined. 93 | pub const SENTINEL: Self = Self::from_montgomery_limbs([u64::MAX; N_64]); 94 | 95 | fn is_sentinel(&self) -> bool { 96 | self.0 .0 == Self::SENTINEL.0 .0 97 | } 98 | 99 | pub fn square(&self) -> Fq { 100 | debug_assert!(!self.is_sentinel()); 101 | Fq(self.0.square()) 102 | } 103 | 104 | pub fn inverse(&self) -> Option { 105 | debug_assert!(!self.is_sentinel()); 106 | 107 | if self == &Fq::ZERO { 108 | return None; 109 | } 110 | 111 | Some(Fq(self.0.inverse()?)) 112 | } 113 | 114 | pub fn add(self, other: &Fq) -> Fq { 115 | debug_assert!(!self.is_sentinel() && !other.is_sentinel()); 116 | Fq(self.0 + other.0) 117 | } 118 | 119 | pub fn sub(self, other: &Fq) -> Fq { 120 | debug_assert!(!self.is_sentinel() && !other.is_sentinel()); 121 | Fq(self.0 - other.0) 122 | } 123 | 124 | pub fn mul(self, other: &Fq) -> Fq { 125 | debug_assert!(!self.is_sentinel() && !other.is_sentinel()); 126 | Fq(self.0 * other.0) 127 | } 128 | 129 | pub fn neg(self) -> Fq { 130 | debug_assert!(!self.is_sentinel()); 131 | Fq(-self.0) 132 | } 133 | } 134 | 135 | impl ConditionallySelectable for Fq { 136 | fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self { 137 | let mut out = [0u64; 4]; 138 | let a_limbs = a.0 .0 .0; 139 | let b_limbs = b.0 .0 .0; 140 | for i in 0..4 { 141 | out[i] = u64::conditional_select(&a_limbs[i], &b_limbs[i], choice); 142 | } 143 | let bigint = BigInt::new(out); 144 | Self(ArkworksFq::new(bigint)) 145 | } 146 | } 147 | 148 | impl ConstantTimeEq for Fq { 149 | fn ct_eq(&self, other: &Fq) -> Choice { 150 | let self_limbs = self.0 .0 .0; 151 | let other_limbs = other.0 .0 .0; 152 | let mut is_equal = true; 153 | for i in 0..4 { 154 | is_equal &= self_limbs[i] == other_limbs[i]; 155 | } 156 | Choice::from(is_equal as u8) 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/fields/fr.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | use rand_core::CryptoRngCore; 3 | 4 | use crate::EncodingError; 5 | 6 | #[cfg(feature = "arkworks")] 7 | pub mod arkworks; 8 | mod ops; 9 | pub mod u32; 10 | 11 | // The u64 backend requires arkworks 12 | #[cfg(feature = "arkworks")] 13 | pub mod u64; 14 | 15 | cfg_if! { 16 | if #[cfg(feature = "arkworks")] { 17 | pub type Fr = u64::Fr; 18 | } else { 19 | pub type Fr = u32::Fr; 20 | } 21 | } 22 | 23 | const B: usize = 251; 24 | const N_8: usize = (B + 7) / 8; 25 | const N_32: usize = (B + 31) / 32; 26 | const N_64: usize = (B + 63) / 64; 27 | 28 | impl Fr { 29 | pub const MODULUS_LIMBS: [u64; N_64] = [ 30 | 13356249993388743167, 31 | 5950279507993463550, 32 | 10965441865914903552, 33 | 336320092672043349, 34 | ]; 35 | 36 | pub const MODULUS_MINUS_ONE_DIV_TWO_LIMBS: [u64; N_64] = [ 37 | 6678124996694371583, 38 | 2975139753996731775, 39 | 14706092969812227584, 40 | 168160046336021674, 41 | ]; 42 | 43 | pub const MODULUS_BIT_SIZE: u32 = 0xfb; 44 | 45 | pub const TRACE_LIMBS: [u64; N_64] = [ 46 | 4478124994494371583, 47 | 2975139753994731775, 48 | 14704092949812227584, 49 | 148140044334021474, 50 | ]; 51 | 52 | pub const TRACE_MINUS_ONE_DIV_TWO_LIMBS: [u64; N_64] = [ 53 | 12542434535201941599, 54 | 1487549874998345887, 55 | 7353044484904113792, 56 | 84080023148010837, 57 | ]; 58 | 59 | pub const TWO_ADICITY: u32 = 0x1; 60 | 61 | pub const MULTIPLICATIVE_GENERATOR: Self = Self::from_montgomery_limbs([ 62 | 11289572479485143824, 63 | 11383437349941080925, 64 | 2288212753973340071, 65 | 82014974407880291, 66 | ]); 67 | 68 | pub const TWO_ADIC_ROOT_OF_UNITY: Self = Self::from_montgomery_limbs([ 69 | 15170730741708341141, 70 | 13470723484578117817, 71 | 12803492244414043445, 72 | 50841023252832411, 73 | ]); 74 | 75 | pub const FIELD_SIZE_POWER_OF_TWO: Self = Self::from_montgomery_limbs([ 76 | 3987543627614508126, 77 | 17742427666091596403, 78 | 14557327917022607905, 79 | 322810149704226881, 80 | ]); 81 | 82 | pub fn from_le_bytes_mod_order(bytes: &[u8]) -> Self { 83 | bytes 84 | .chunks(N_8) 85 | .map(|x| { 86 | let mut padded = [0u8; N_8]; 87 | padded[..x.len()].copy_from_slice(x); 88 | Self::from_raw_bytes(&padded) 89 | }) // [X, 2^(256) * X, ...] 90 | .rev() 91 | .fold(Self::ZERO, |acc, x| { 92 | acc * (Self::FIELD_SIZE_POWER_OF_TWO) + x 93 | }) // let acc = 94 | } 95 | 96 | /// Convert bytes into an Fr element, returning None if these bytes are not already reduced. 97 | /// 98 | /// This means that values that cannot be produced by encoding a field element will return 99 | /// None, enforcing canonical serialization. 100 | pub fn from_bytes_checked(bytes: &[u8; N_8]) -> Result { 101 | let reduced = Self::from_raw_bytes(bytes); 102 | if reduced.to_bytes_le() == *bytes { 103 | Ok(reduced) 104 | } else { 105 | Err(EncodingError::InvalidEncoding) 106 | } 107 | } 108 | 109 | pub fn to_bytes(&self) -> [u8; N_8] { 110 | self.to_bytes_le() 111 | } 112 | 113 | /// Sample a random field element uniformly. 114 | pub fn rand(rng: &mut R) -> Self { 115 | // Sample wide, reduce 116 | let bytes = { 117 | let mut out = [0u8; N_8 + 16]; 118 | rng.fill_bytes(&mut out); 119 | out 120 | }; 121 | Self::from_le_bytes_mod_order(&bytes) 122 | } 123 | } 124 | 125 | #[cfg(test)] 126 | mod test { 127 | use super::*; 128 | 129 | #[test] 130 | fn test_from_bytes_checked() { 131 | assert_eq!(Fr::from_bytes_checked(&[0; N_8]), Ok(Fr::ZERO)); 132 | assert!(Fr::from_bytes_checked(&[0xFF; N_8]).is_err()); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/fields/fr/ops.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cmp::Ordering, 3 | hash::Hash, 4 | iter::{Product, Sum}, 5 | ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign}, 6 | }; 7 | 8 | use crate::Fr; 9 | 10 | impl From for Fr { 11 | fn from(other: u128) -> Self { 12 | Self::from_le_limbs([other as u64, (other >> 64) as u64, 0, 0]) 13 | } 14 | } 15 | 16 | impl From for Fr { 17 | fn from(other: u64) -> Self { 18 | u128::from(other).into() 19 | } 20 | } 21 | 22 | impl From for Fr { 23 | fn from(other: u32) -> Self { 24 | u128::from(other).into() 25 | } 26 | } 27 | 28 | impl From for Fr { 29 | fn from(other: u16) -> Self { 30 | u128::from(other).into() 31 | } 32 | } 33 | 34 | impl From for Fr { 35 | fn from(other: u8) -> Self { 36 | u128::from(other).into() 37 | } 38 | } 39 | 40 | impl From for Fr { 41 | fn from(other: bool) -> Self { 42 | u128::from(other).into() 43 | } 44 | } 45 | 46 | impl Neg for Fr { 47 | type Output = Self; 48 | 49 | #[inline] 50 | #[must_use] 51 | fn neg(self) -> Self { 52 | let neg = self.neg(); 53 | neg 54 | } 55 | } 56 | 57 | impl<'a> AddAssign<&'a Self> for Fr { 58 | #[inline] 59 | fn add_assign(&mut self, other: &Self) { 60 | *self = self.add(other); 61 | } 62 | } 63 | 64 | impl AddAssign for Fr { 65 | #[inline(always)] 66 | fn add_assign(&mut self, other: Self) { 67 | *self = self.add(&other); 68 | } 69 | } 70 | 71 | impl<'a> AddAssign<&'a mut Self> for Fr { 72 | #[inline(always)] 73 | fn add_assign(&mut self, other: &'a mut Self) { 74 | *self = self.add(other); 75 | } 76 | } 77 | 78 | impl Add for Fr { 79 | type Output = Self; 80 | 81 | #[inline] 82 | fn add(self, other: Self) -> Self { 83 | self.add(&other) 84 | } 85 | } 86 | 87 | impl<'a> Add<&'a Fr> for Fr { 88 | type Output = Self; 89 | 90 | #[inline] 91 | fn add(self, other: &Self) -> Self { 92 | self.add(other) 93 | } 94 | } 95 | 96 | impl<'a> Add<&'a mut Self> for Fr { 97 | type Output = Self; 98 | 99 | #[inline] 100 | fn add(self, other: &'a mut Self) -> Self { 101 | self.add(other) 102 | } 103 | } 104 | 105 | impl<'a> SubAssign<&'a Self> for Fr { 106 | #[inline] 107 | fn sub_assign(&mut self, other: &Self) { 108 | *self = self.sub(other); 109 | } 110 | } 111 | 112 | impl SubAssign for Fr { 113 | #[inline(always)] 114 | fn sub_assign(&mut self, other: Self) { 115 | *self = self.sub(&other); 116 | } 117 | } 118 | 119 | impl<'a> SubAssign<&'a mut Self> for Fr { 120 | #[inline(always)] 121 | fn sub_assign(&mut self, other: &'a mut Self) { 122 | *self = self.sub(other); 123 | } 124 | } 125 | 126 | impl Sub for Fr { 127 | type Output = Self; 128 | 129 | #[inline] 130 | fn sub(self, other: Self) -> Self { 131 | self.sub(&other) 132 | } 133 | } 134 | 135 | impl<'a> Sub<&'a Fr> for Fr { 136 | type Output = Self; 137 | 138 | #[inline] 139 | fn sub(self, other: &Self) -> Self { 140 | self.sub(other) 141 | } 142 | } 143 | 144 | impl<'a> Sub<&'a mut Self> for Fr { 145 | type Output = Self; 146 | 147 | #[inline] 148 | fn sub(self, other: &'a mut Self) -> Self { 149 | self.sub(other) 150 | } 151 | } 152 | 153 | impl<'a> MulAssign<&'a Self> for Fr { 154 | fn mul_assign(&mut self, other: &Self) { 155 | *self = self.mul(other); 156 | } 157 | } 158 | 159 | impl core::ops::MulAssign for Fr { 160 | #[inline(always)] 161 | fn mul_assign(&mut self, other: Self) { 162 | *self = self.mul(&other); 163 | } 164 | } 165 | 166 | impl<'a> core::ops::MulAssign<&'a mut Self> for Fr { 167 | #[inline(always)] 168 | fn mul_assign(&mut self, other: &'a mut Self) { 169 | *self = self.mul(other); 170 | } 171 | } 172 | 173 | impl Mul for Fr { 174 | type Output = Self; 175 | 176 | #[inline(always)] 177 | fn mul(self, other: Self) -> Self { 178 | self.mul(&other) 179 | } 180 | } 181 | 182 | impl<'a> Mul<&'a Fr> for Fr { 183 | type Output = Self; 184 | 185 | #[inline] 186 | fn mul(self, other: &Self) -> Self { 187 | self.mul(other) 188 | } 189 | } 190 | 191 | impl<'a> Mul<&'a mut Self> for Fr { 192 | type Output = Self; 193 | 194 | #[inline(always)] 195 | fn mul(self, other: &'a mut Self) -> Self { 196 | self.mul(other) 197 | } 198 | } 199 | 200 | impl<'a> DivAssign<&'a Self> for Fr { 201 | #[inline(always)] 202 | fn div_assign(&mut self, other: &Self) { 203 | self.mul_assign(&other.inverse().unwrap()); 204 | } 205 | } 206 | 207 | impl DivAssign for Fr { 208 | #[inline(always)] 209 | fn div_assign(&mut self, other: Self) { 210 | self.div_assign(&other) 211 | } 212 | } 213 | 214 | impl<'a> DivAssign<&'a mut Self> for Fr { 215 | #[inline(always)] 216 | fn div_assign(&mut self, other: &'a mut Self) { 217 | self.div_assign(&*other) 218 | } 219 | } 220 | 221 | impl Div for Fr { 222 | type Output = Self; 223 | 224 | #[inline(always)] 225 | fn div(mut self, other: Self) -> Self { 226 | self.div_assign(&other); 227 | self 228 | } 229 | } 230 | 231 | impl<'a> Div<&'a Fr> for Fr { 232 | type Output = Self; 233 | 234 | #[inline] 235 | fn div(mut self, other: &Self) -> Self { 236 | self.mul_assign(&other.inverse().unwrap()); 237 | self 238 | } 239 | } 240 | 241 | impl<'a> Div<&'a mut Self> for Fr { 242 | type Output = Self; 243 | 244 | #[inline(always)] 245 | fn div(mut self, other: &'a mut Self) -> Self { 246 | self.div_assign(&*other); 247 | self 248 | } 249 | } 250 | 251 | impl Sum for Fr { 252 | fn sum>(iter: I) -> Self { 253 | iter.fold(Self::ZERO, Add::add) 254 | } 255 | } 256 | 257 | impl<'a> Sum<&'a Self> for Fr { 258 | fn sum>(iter: I) -> Self { 259 | iter.fold(Self::ZERO, Add::add) 260 | } 261 | } 262 | 263 | impl Product for Fr { 264 | fn product>(iter: I) -> Self { 265 | iter.fold(Self::ZERO, Mul::mul) 266 | } 267 | } 268 | 269 | impl<'a> Product<&'a Self> for Fr { 270 | fn product>(iter: I) -> Self { 271 | iter.fold(Self::ZERO, Mul::mul) 272 | } 273 | } 274 | 275 | impl Ord for Fr { 276 | #[inline(always)] 277 | fn cmp(&self, other: &Self) -> Ordering { 278 | let mut left = self.to_le_limbs(); 279 | let mut right = other.to_le_limbs(); 280 | left.reverse(); 281 | right.reverse(); 282 | left.cmp(&right) 283 | } 284 | } 285 | 286 | impl PartialOrd for Fr { 287 | #[inline(always)] 288 | fn partial_cmp(&self, other: &Self) -> Option { 289 | Some(self.cmp(other)) 290 | } 291 | } 292 | 293 | impl Hash for Fr { 294 | fn hash(&self, state: &mut H) { 295 | state.write(&self.to_bytes_le()) 296 | } 297 | } 298 | 299 | impl Default for Fr { 300 | fn default() -> Self { 301 | Self::ZERO 302 | } 303 | } 304 | 305 | impl core::fmt::Debug for Fr { 306 | fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { 307 | let bytes = { 308 | let mut out = self.to_bytes_le(); 309 | out.reverse(); 310 | out 311 | }; 312 | let mut hex_chars = [0u8; 64]; 313 | hex::encode_to_slice(&bytes, &mut hex_chars) 314 | .expect("not enough space to write hex characters"); 315 | // Safety: hex characters will be valid UTF8. 316 | write!(f, "Fr(0x{})", unsafe { 317 | core::str::from_utf8_unchecked(&hex_chars) 318 | }) 319 | } 320 | } 321 | -------------------------------------------------------------------------------- /src/fields/fr/u32.rs: -------------------------------------------------------------------------------- 1 | pub mod fiat; 2 | pub mod wrapper; 3 | 4 | pub use wrapper::Fr; 5 | -------------------------------------------------------------------------------- /src/fields/fr/u32/wrapper.rs: -------------------------------------------------------------------------------- 1 | use super::{ 2 | super::{B, N_32, N_64, N_8}, 3 | fiat, 4 | }; 5 | 6 | const N: usize = N_32; 7 | 8 | #[derive(Copy, Clone)] 9 | pub struct Fr(fiat::FrMontgomeryDomainFieldElement); 10 | 11 | impl PartialEq for Fr { 12 | fn eq(&self, other: &Self) -> bool { 13 | let sub = self.sub(other); 14 | let mut check_word = 0; 15 | fiat::fr_nonzero(&mut check_word, &sub.0 .0); 16 | check_word == 0 17 | } 18 | } 19 | 20 | impl Eq for Fr {} 21 | 22 | impl zeroize::Zeroize for Fr { 23 | fn zeroize(&mut self) { 24 | self.0 .0.zeroize() 25 | } 26 | } 27 | 28 | impl Fr { 29 | pub(crate) fn from_le_limbs(limbs: [u64; N_64]) -> Fr { 30 | let limbs = { 31 | let mut out = [0u32; N]; 32 | for i in 0..N_64 { 33 | out[2 * i] = (limbs[i] & 0xFFFF_FFFF_FFFF_FFFF) as u32; 34 | out[2 * i + 1] = (limbs[i] >> 32) as u32; 35 | } 36 | out 37 | }; 38 | let x_non_monty = fiat::FrNonMontgomeryDomainFieldElement(limbs); 39 | let mut x = fiat::FrMontgomeryDomainFieldElement([0; N]); 40 | fiat::fr_to_montgomery(&mut x, &x_non_monty); 41 | Self(x) 42 | } 43 | 44 | pub(crate) fn from_raw_bytes(bytes: &[u8; N_8]) -> Fr { 45 | let mut x_non_montgomery = fiat::FrNonMontgomeryDomainFieldElement([0; N]); 46 | let mut x = fiat::FrMontgomeryDomainFieldElement([0; N]); 47 | 48 | fiat::fr_from_bytes(&mut x_non_montgomery.0, &bytes); 49 | fiat::fr_to_montgomery(&mut x, &x_non_montgomery); 50 | 51 | Self(x) 52 | } 53 | 54 | pub(crate) fn to_le_limbs(&self) -> [u64; N_64] { 55 | let mut x_non_montgomery = fiat::FrNonMontgomeryDomainFieldElement([0; N]); 56 | fiat::fr_from_montgomery(&mut x_non_montgomery, &self.0); 57 | let limbs = x_non_montgomery.0; 58 | let mut out = [0u64; N_64]; 59 | for i in 0..N_64 { 60 | out[i] = (limbs[2 * i] as u64) | ((limbs[2 * i + 1] as u64) << 32); 61 | } 62 | out 63 | } 64 | 65 | pub fn to_bytes_le(&self) -> [u8; N_8] { 66 | let mut bytes = [0u8; N_8]; 67 | let mut x_non_montgomery = fiat::FrNonMontgomeryDomainFieldElement([0; N]); 68 | fiat::fr_from_montgomery(&mut x_non_montgomery, &self.0); 69 | fiat::fr_to_bytes(&mut bytes, &x_non_montgomery.0); 70 | bytes 71 | } 72 | 73 | const fn from_montgomery_limbs_backend(limbs: [u32; N]) -> Fr { 74 | Self(fiat::FrMontgomeryDomainFieldElement(limbs)) 75 | } 76 | 77 | pub(crate) const fn from_montgomery_limbs(limbs: [u64; N_64]) -> Fr { 78 | Self::from_montgomery_limbs_backend([ 79 | limbs[0] as u32, 80 | (limbs[0] >> 32) as u32, 81 | limbs[1] as u32, 82 | (limbs[1] >> 32) as u32, 83 | limbs[2] as u32, 84 | (limbs[2] >> 32) as u32, 85 | limbs[3] as u32, 86 | (limbs[3] >> 32) as u32, 87 | ]) 88 | } 89 | 90 | pub const ZERO: Fr = Self(fiat::FrMontgomeryDomainFieldElement([0; N])); 91 | 92 | pub const ONE: Fr = Self(fiat::FrMontgomeryDomainFieldElement([ 93 | 3498574902, 3872500570, 2604314180, 2497411308, 588265454, 3867012838, 3735373809, 66463618, 94 | ])); 95 | 96 | pub fn square(&self) -> Fr { 97 | let mut result = fiat::FrMontgomeryDomainFieldElement([0; N]); 98 | fiat::fr_square(&mut result, &self.0); 99 | Self(result) 100 | } 101 | 102 | pub fn inverse(&self) -> Option { 103 | if self == &Self::ZERO { 104 | return None; 105 | } 106 | 107 | const I: usize = (49 * B + 57) / 17; 108 | 109 | let mut a = fiat::FrNonMontgomeryDomainFieldElement([0; N]); 110 | fiat::fr_from_montgomery(&mut a, &self.0); 111 | let mut d = 1; 112 | let mut f: [u32; N + 1] = [0u32; N + 1]; 113 | fiat::fr_msat(&mut f); 114 | let mut g: [u32; N + 1] = [0u32; N + 1]; 115 | let mut v: [u32; N] = [0u32; N]; 116 | let mut r: [u32; N] = Self::ONE.0 .0; 117 | let mut i = 0; 118 | let mut j = 0; 119 | 120 | while j < N { 121 | g[j] = a[j]; 122 | j += 1; 123 | } 124 | 125 | let mut out1: u32 = 0; 126 | let mut out2: [u32; N + 1] = [0; N + 1]; 127 | let mut out3: [u32; N + 1] = [0; N + 1]; 128 | let mut out4: [u32; N] = [0; N]; 129 | let mut out5: [u32; N] = [0; N]; 130 | let mut out6: u32 = 0; 131 | let mut out7: [u32; N + 1] = [0; N + 1]; 132 | let mut out8: [u32; N + 1] = [0; N + 1]; 133 | let mut out9: [u32; N] = [0; N]; 134 | let mut out10: [u32; N] = [0; N]; 135 | 136 | while i < I - I % 2 { 137 | fiat::fr_divstep( 138 | &mut out1, &mut out2, &mut out3, &mut out4, &mut out5, d, &f, &g, &v, &r, 139 | ); 140 | fiat::fr_divstep( 141 | &mut out6, &mut out7, &mut out8, &mut out9, &mut out10, out1, &out2, &out3, &out4, 142 | &out5, 143 | ); 144 | d = out6; 145 | f = out7; 146 | g = out8; 147 | v = out9; 148 | r = out10; 149 | i += 2; 150 | } 151 | 152 | if I % 2 != 0 { 153 | fiat::fr_divstep( 154 | &mut out1, &mut out2, &mut out3, &mut out4, &mut out5, d, &f, &g, &v, &r, 155 | ); 156 | v = out4; 157 | f = out2; 158 | } 159 | 160 | let s = ((f[f.len() - 1] >> (32 - 1)) & 1) as u8; 161 | let mut neg = fiat::FrMontgomeryDomainFieldElement([0; N]); 162 | fiat::fr_opp(&mut neg, &fiat::FrMontgomeryDomainFieldElement(v)); 163 | 164 | let mut v_prime: [u32; N] = [0u32; N]; 165 | fiat::fr_selectznz(&mut v_prime, s, &v, &neg.0); 166 | 167 | let mut pre_comp: [u32; N] = [0u32; N]; 168 | fiat::fr_divstep_precomp(&mut pre_comp); 169 | 170 | let mut result = fiat::FrMontgomeryDomainFieldElement([0; N]); 171 | fiat::fr_mul( 172 | &mut result, 173 | &fiat::FrMontgomeryDomainFieldElement(v_prime), 174 | &fiat::FrMontgomeryDomainFieldElement(pre_comp), 175 | ); 176 | 177 | Some(Fr(result)) 178 | } 179 | 180 | pub fn add(self, other: &Fr) -> Fr { 181 | let mut result = fiat::FrMontgomeryDomainFieldElement([0; N]); 182 | fiat::fr_add(&mut result, &self.0, &other.0); 183 | Fr(result) 184 | } 185 | 186 | pub fn sub(self, other: &Fr) -> Fr { 187 | let mut result = fiat::FrMontgomeryDomainFieldElement([0; N]); 188 | fiat::fr_sub(&mut result, &self.0, &other.0); 189 | Fr(result) 190 | } 191 | 192 | pub fn mul(self, other: &Fr) -> Fr { 193 | let mut result = fiat::FrMontgomeryDomainFieldElement([0; N]); 194 | fiat::fr_mul(&mut result, &self.0, &other.0); 195 | Fr(result) 196 | } 197 | 198 | pub fn neg(self) -> Fr { 199 | let mut result = fiat::FrMontgomeryDomainFieldElement([0; N]); 200 | fiat::fr_opp(&mut result, &self.0); 201 | Fr(result) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /src/fields/fr/u64.rs: -------------------------------------------------------------------------------- 1 | pub mod wrapper; 2 | 3 | pub use wrapper::Fr; 4 | -------------------------------------------------------------------------------- /src/fields/fr/u64/wrapper.rs: -------------------------------------------------------------------------------- 1 | use ark_ed_on_bls12_377::Fr as ArkworksFr; 2 | use ark_ff::{biginteger::BigInt, Field, PrimeField}; 3 | use ark_serialize::CanonicalSerialize; 4 | 5 | use super::super::{N_64, N_8}; 6 | 7 | const N: usize = N_64; 8 | 9 | #[derive(Copy, Clone)] 10 | pub struct Fr(ArkworksFr); 11 | 12 | impl PartialEq for Fr { 13 | fn eq(&self, other: &Self) -> bool { 14 | self.0 == other.0 15 | } 16 | } 17 | 18 | impl Eq for Fr {} 19 | 20 | impl zeroize::Zeroize for Fr { 21 | fn zeroize(&mut self) { 22 | self.0 .0.zeroize() 23 | } 24 | } 25 | 26 | impl Fr { 27 | pub(crate) fn from_le_limbs(limbs: [u64; N_64]) -> Fr { 28 | let mut bytes = [0u8; N_8]; 29 | for i in 0..N_64 { 30 | let this_byte = limbs[i].to_le_bytes(); 31 | for j in 0..8 { 32 | bytes[8 * i + j] = this_byte[j]; 33 | } 34 | } 35 | 36 | Self::from_raw_bytes(&bytes) 37 | } 38 | 39 | pub(crate) fn from_raw_bytes(bytes: &[u8; N_8]) -> Fr { 40 | Self(ArkworksFr::from_le_bytes_mod_order(bytes)) 41 | } 42 | 43 | pub(crate) fn to_le_limbs(&self) -> [u64; N_64] { 44 | let le_bytes = self.to_bytes_le(); 45 | let mut out = [0u64; N_64]; 46 | for i in 0..N_64 { 47 | out[i] = u64::from_le_bytes([ 48 | le_bytes[8 * i], 49 | le_bytes[8 * i + 1], 50 | le_bytes[8 * i + 2], 51 | le_bytes[8 * i + 3], 52 | le_bytes[8 * i + 4], 53 | le_bytes[8 * i + 5], 54 | le_bytes[8 * i + 6], 55 | le_bytes[8 * i + 7], 56 | ]); 57 | } 58 | out 59 | } 60 | 61 | pub fn to_bytes_le(&self) -> [u8; N_8] { 62 | let mut bytes = [0u8; 32]; 63 | self.0 64 | .serialize_compressed(&mut bytes[..]) 65 | .expect("serialization into array should be infallible"); 66 | bytes 67 | } 68 | 69 | pub(crate) const fn from_montgomery_limbs(limbs: [u64; N]) -> Fr { 70 | Self(ArkworksFr::new_unchecked(BigInt::new(limbs))) 71 | } 72 | 73 | pub const ZERO: Self = Self(ArkworksFr::new(BigInt::new([0; N]))); 74 | pub const ONE: Self = Self(ArkworksFr::new(BigInt::one())); 75 | 76 | pub fn square(&self) -> Fr { 77 | Fr(self.0.square()) 78 | } 79 | 80 | pub fn inverse(&self) -> Option { 81 | if self == &Self::ZERO { 82 | return None; 83 | } 84 | 85 | Some(Fr(self.0.inverse()?)) 86 | } 87 | 88 | pub fn add(self, other: &Fr) -> Fr { 89 | Fr(self.0 + other.0) 90 | } 91 | 92 | pub fn sub(self, other: &Fr) -> Fr { 93 | Fr(self.0 - other.0) 94 | } 95 | 96 | pub fn mul(self, other: &Fr) -> Fr { 97 | Fr(self.0 * other.0) 98 | } 99 | 100 | pub fn neg(self) -> Fr { 101 | Fr(-self.0) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | //! `decaf377` [instantiates Decaf over the BLS12-377 scalar 3 | //! field](https://penumbra.zone/crypto/primitives/decaf377.html). 4 | //! 5 | use cfg_if::cfg_if; 6 | 7 | pub mod fields; 8 | pub use fields::{fp::Fp, fq::Fq, fr::Fr}; 9 | mod sign; 10 | 11 | mod error; 12 | pub use error::EncodingError; 13 | 14 | cfg_if! { 15 | if #[cfg(feature = "arkworks")] { 16 | mod ark_curve; 17 | 18 | pub use ark_curve::{Element, Encoding, ZETA}; 19 | 20 | pub use ark_curve::bls12_377::Bls12_377; 21 | 22 | #[cfg(feature = "r1cs")] 23 | pub use ark_curve::r1cs; 24 | 25 | 26 | } else { 27 | mod min_curve; 28 | 29 | pub use min_curve::{Element, Encoding, ZETA}; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/min_curve/constants.rs: -------------------------------------------------------------------------------- 1 | use crate::Fq; 2 | 3 | pub const ZETA: Fq = Fq::from_montgomery_limbs([ 4 | 5947794125541564500, 5 | 11292571455564096885, 6 | 11814268415718120036, 7 | 155746270000486182, 8 | ]); 9 | 10 | pub const ZETA_TO_TRACE: Fq = Fq::from_montgomery_limbs([ 11 | 6282505393754313363, 12 | 14378628227555923904, 13 | 9804873068900332207, 14 | 302335131180501866, 15 | ]); 16 | 17 | /// COEFF_A = -1 18 | pub const COEFF_A: Fq = Fq::from_montgomery_limbs([ 19 | 10157024534604021774, 20 | 16668528035959406606, 21 | 5322190058819395602, 22 | 387181115924875961, 23 | ]); 24 | 25 | /// COEFF_D = 3021 26 | pub const COEFF_D: Fq = Fq::from_montgomery_limbs([ 27 | 15008245758212136496, 28 | 17341409599856531410, 29 | 648869460136961410, 30 | 719771289660577536, 31 | ]); 32 | 33 | /// -2 COEFF_D / COEFF_A = 6042 34 | pub const COEFF_K: Fq = Fq::from_montgomery_limbs([ 35 | 10844245690243005535, 36 | 9774967673803681700, 37 | 12776203677742963460, 38 | 94262208632981673, 39 | ]); 40 | -------------------------------------------------------------------------------- /src/min_curve/encoding.rs: -------------------------------------------------------------------------------- 1 | use crate::EncodingError; 2 | 3 | #[derive(Copy, Clone, Default, Eq, Ord, PartialOrd, PartialEq, Debug)] 4 | pub struct Encoding(pub [u8; 32]); 5 | -------------------------------------------------------------------------------- /src/min_curve/invsqrt.rs: -------------------------------------------------------------------------------- 1 | use subtle::{ConditionallySelectable, ConstantTimeEq}; 2 | 3 | use crate::Fq; 4 | 5 | use crate::min_curve::constants::ZETA; 6 | 7 | impl Fq { 8 | /// For square elements, calculate their square root, otherwise return an undefined element. 9 | /// 10 | /// Based on https://www.ietf.org/archive/id/draft-irtf-cfrg-hash-to-curve-10.html#name-constant-time-tonelli-shanks 11 | fn our_sqrt(&self) -> Self { 12 | // Constants c1,...,c5 used for square root computation as defined in the above Appendix: 13 | // c1 = TWO_ADICITY 14 | // c2 is not directly used in the computation, it's used to compute c3 15 | // c3 = TRACE_MINUS_ONE_DIV_TWO_LIMBS; 16 | // c4 is not directly used in the computation, but should match ZETA- 17 | // c5 = c4 ^ c2 18 | // c5 = QUADRATIC_NON_RESIDUE_TO_TRACE 19 | 20 | // Step 1: z = x^c3 21 | let mut z = self.pow_le_limbs(&Fq::TRACE_MINUS_ONE_DIV_TWO_LIMBS); 22 | 23 | // Step 2: t = z * z * x 24 | let mut t = z * z * self; 25 | 26 | // Step 3: z = z * x; 27 | z = z * self; 28 | 29 | // Step 4: b = t 30 | let mut b = t; 31 | 32 | // Step 5: c = c5 33 | let mut c = Fq::QUADRATIC_NON_RESIDUE_TO_TRACE; 34 | 35 | // Step 6: for i in (c1, c1 - 1, ..., 2): 36 | for i in (2..=Fq::TWO_ADICITY).rev() { 37 | // Step 7: for j in (1, 2, ..., i - 2): 38 | for _j in 1..=i - 2 { 39 | // Step 8: b = b * b 40 | b = b * b; 41 | } 42 | 43 | // Step 9: z = CMOV(z, z * c, b != 1) 44 | z = Fq::conditional_select(&z, &(z * c), !b.ct_eq(&Self::ONE)); 45 | 46 | // Step 10: c = c * c 47 | c = c * c; 48 | 49 | // Step 11: t = CMOV(t, t * c, b != 1) 50 | t = Fq::conditional_select(&t, &(t * c), !b.ct_eq(&Self::ONE)); 51 | 52 | // Step 12: b = t 53 | b = t; 54 | } 55 | 56 | // Step 13: return z 57 | z 58 | } 59 | 60 | fn pow_le_limbs(&self, limbs: &[u64]) -> Self { 61 | let mut acc = Self::ONE; 62 | let mut insert = *self; 63 | for limb in limbs { 64 | for i in 0..64 { 65 | if (limb >> i) & 1 == 1 { 66 | acc *= insert; 67 | } 68 | insert *= insert; 69 | } 70 | } 71 | acc 72 | } 73 | 74 | /// Computes the square root of a ratio of field elements, returning: 75 | /// 76 | /// - `(true, sqrt(num/den))` if `num` and `den` are both nonzero and `num/den` is square; 77 | /// - `(true, 0)` if `num` is zero; 78 | /// - `(false, 0)` if `den` is zero; 79 | /// - `(false, sqrt(zeta*num/den))` if `num` and `den` are both nonzero and `num/den` is nonsquare; 80 | pub fn non_arkworks_sqrt_ratio_zeta(num: &Self, den: &Self) -> (bool, Self) { 81 | if num == &Fq::ZERO { 82 | return (true, *num); 83 | } 84 | if den == &Fq::ZERO { 85 | return (false, *den); 86 | } 87 | let x = *num / *den; 88 | // Because num was not zero, this will only be 1 or -1 89 | let symbol = x.pow_le_limbs(&Fq::MODULUS_MINUS_ONE_DIV_TWO_LIMBS); 90 | if symbol == Self::ONE { 91 | (true, x.our_sqrt()) 92 | } else { 93 | (false, (ZETA * x).our_sqrt()) 94 | } 95 | } 96 | } 97 | 98 | #[cfg(all(test, feature = "arkworks"))] 99 | mod tests { 100 | use super::*; 101 | use ark_ff::Field; 102 | use ark_ff::PrimeField; 103 | use proptest::prelude::*; 104 | 105 | fn fq_strategy() -> impl Strategy { 106 | any::<[u8; 32]>() 107 | .prop_map(|bytes| Fq::from_le_bytes_mod_order(&bytes[..])) 108 | .boxed() 109 | } 110 | 111 | proptest! { 112 | #![proptest_config(ProptestConfig::with_cases(10000))] 113 | #[test] 114 | fn sqrt_ratio_zeta(u in fq_strategy(), v in fq_strategy()) { 115 | if u == Fq::ZERO { 116 | assert_eq!(Fq::non_arkworks_sqrt_ratio_zeta(&u, &v), (true, u)); 117 | } else if v == Fq::ZERO { 118 | assert_eq!(Fq::non_arkworks_sqrt_ratio_zeta(&u, &v), (false, v)); 119 | } else { 120 | let (was_square, sqrt_zeta_uv) = Fq::non_arkworks_sqrt_ratio_zeta(&u, &v); 121 | let zeta_uv = sqrt_zeta_uv * sqrt_zeta_uv; 122 | if was_square { 123 | // check zeta_uv = u/v 124 | assert_eq!(u, v * zeta_uv); 125 | } else { 126 | // check zeta_uv = zeta * u / v 127 | assert_eq!(ZETA * u, v * zeta_uv); 128 | } 129 | } 130 | } 131 | } 132 | 133 | #[test] 134 | fn sqrt_ratio_edge_cases() { 135 | // u = 0 136 | assert_eq!( 137 | Fq::non_arkworks_sqrt_ratio_zeta(&Fq::ZERO, &Fq::ONE), 138 | (true, Fq::ZERO) 139 | ); 140 | 141 | // v = 0 142 | assert_eq!( 143 | Fq::non_arkworks_sqrt_ratio_zeta(&Fq::ONE, &Fq::ZERO), 144 | (false, Fq::ZERO) 145 | ); 146 | } 147 | 148 | proptest! { 149 | #[test] 150 | fn sqrt_matches_arkworks(x in fq_strategy()) { 151 | let arkworks_sqrt = x.sqrt(); 152 | let our_sqrt = x.our_sqrt(); 153 | if arkworks_sqrt.is_some() { 154 | assert_eq!(arkworks_sqrt.unwrap(), our_sqrt); 155 | } 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /src/min_curve/mod.rs: -------------------------------------------------------------------------------- 1 | mod constants; 2 | pub mod element; 3 | pub mod encoding; 4 | mod invsqrt; 5 | mod ops; 6 | 7 | pub use constants::ZETA; 8 | pub use element::Element; 9 | pub use encoding::Encoding; 10 | -------------------------------------------------------------------------------- /src/min_curve/ops.rs: -------------------------------------------------------------------------------- 1 | use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 2 | 3 | use crate::{min_curve::element::Element, Fr}; 4 | 5 | // Element addition 6 | 7 | impl<'a, 'b> Add<&'b Element> for &'a Element { 8 | type Output = Element; 9 | 10 | fn add(self, other: &'b Element) -> Element { 11 | self + *other 12 | } 13 | } 14 | 15 | impl<'b> Add<&'b Element> for Element { 16 | type Output = Element; 17 | fn add(self, other: &'b Element) -> Element { 18 | self + *other 19 | } 20 | } 21 | 22 | impl<'a> Add for &'a Element { 23 | type Output = Element; 24 | fn add(self, other: Element) -> Element { 25 | *self + other 26 | } 27 | } 28 | 29 | impl<'b> AddAssign<&'b Element> for Element { 30 | fn add_assign(&mut self, other: &'b Element) { 31 | *self = *self + other 32 | } 33 | } 34 | 35 | impl AddAssign for Element { 36 | fn add_assign(&mut self, other: Element) { 37 | *self += &other; 38 | } 39 | } 40 | 41 | // Element subtraction 42 | 43 | impl Sub for Element { 44 | type Output = Element; 45 | 46 | fn sub(self, other: Element) -> Element { 47 | self + other.neg() 48 | } 49 | } 50 | 51 | impl<'a, 'b> Sub<&'b Element> for &'a Element { 52 | type Output = Element; 53 | 54 | fn sub(self, other: &'b Element) -> Element { 55 | *self - *other 56 | } 57 | } 58 | 59 | impl<'b> Sub<&'b Element> for Element { 60 | type Output = Element; 61 | 62 | fn sub(self, other: &'b Element) -> Element { 63 | self - *other 64 | } 65 | } 66 | 67 | impl<'a> Sub for &'a Element { 68 | type Output = Element; 69 | 70 | fn sub(self, other: Element) -> Element { 71 | *self - other 72 | } 73 | } 74 | 75 | impl<'b> SubAssign<&'b Element> for Element { 76 | fn sub_assign(&mut self, other: &'b Element) { 77 | *self = *self - other; 78 | } 79 | } 80 | 81 | impl SubAssign for Element { 82 | fn sub_assign(&mut self, other: Element) { 83 | *self -= &other; 84 | } 85 | } 86 | 87 | /// Scalar multiplication 88 | 89 | impl Mul for Element { 90 | type Output = Self; 91 | 92 | fn mul(self, rhs: Fr) -> Self::Output { 93 | Self::scalar_mul_vartime(self, &rhs.to_le_limbs()) 94 | } 95 | } 96 | 97 | impl<'b> MulAssign<&'b Fr> for Element { 98 | fn mul_assign(&mut self, rhs: &'b Fr) { 99 | *self = *self * rhs; 100 | } 101 | } 102 | 103 | impl MulAssign for Element { 104 | fn mul_assign(&mut self, other: Fr) { 105 | *self *= &other; 106 | } 107 | } 108 | 109 | impl<'a, 'b> Mul<&'b Fr> for &'a Element { 110 | type Output = Element; 111 | 112 | fn mul(self, scalar: &'b Fr) -> Element { 113 | *scalar * *self 114 | } 115 | } 116 | 117 | impl<'a, 'b> Mul<&'b Element> for &'a Fr { 118 | type Output = Element; 119 | 120 | fn mul(self, point: &'b Element) -> Element { 121 | *point * *self 122 | } 123 | } 124 | 125 | impl<'b> Mul<&'b Fr> for Element { 126 | type Output = Self; 127 | 128 | fn mul(self, other: &'b Fr) -> Element { 129 | self * *other 130 | } 131 | } 132 | 133 | impl<'a> Mul for &'a Element { 134 | type Output = Element; 135 | 136 | fn mul(self, other: Fr) -> Element { 137 | *self * other 138 | } 139 | } 140 | 141 | impl<'b> Mul<&'b Element> for Fr { 142 | type Output = Element; 143 | 144 | fn mul(self, other: &'b Element) -> Element { 145 | *other * self 146 | } 147 | } 148 | 149 | impl<'a> Mul for &'a Fr { 150 | type Output = Element; 151 | 152 | fn mul(self, other: Element) -> Element { 153 | other * *self 154 | } 155 | } 156 | 157 | impl Mul for Fr { 158 | type Output = Element; 159 | 160 | fn mul(self, other: Element) -> Element { 161 | other * self 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/sign.rs: -------------------------------------------------------------------------------- 1 | use crate::Fq; 2 | 3 | pub trait Sign: core::ops::Neg + Sized { 4 | fn is_nonnegative(&self) -> bool; 5 | 6 | fn is_negative(&self) -> bool { 7 | !self.is_nonnegative() 8 | } 9 | 10 | fn abs(self) -> Self { 11 | if self.is_nonnegative() { 12 | self 13 | } else { 14 | -self 15 | } 16 | } 17 | } 18 | 19 | impl Sign for Fq { 20 | fn is_nonnegative(&self) -> bool { 21 | (self.to_le_limbs()[0] & 1) == 0 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/encoding.proptest-regressions: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 82945fc5ee2715e3e8cd35890759aefcdd76b3381f6379e26ba009933f55abd3 # shrinks to bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5] 8 | cc 264ed2b3f5cb8d69cb8ac150a1f1150a65b7a064170a52f4bba9a4d450f6369d # shrinks to bytes = [12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] 9 | cc ba572d5b5a4a03b1ab05649faff2f4251da79a38c91ee13d3fb9b54d9b0c6b32 # shrinks to bytes = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 172, 18] 10 | -------------------------------------------------------------------------------- /tests/encoding.rs: -------------------------------------------------------------------------------- 1 | use core::convert::TryFrom; 2 | 3 | use proptest::prelude::*; 4 | 5 | use decaf377::{Element, Encoding, Fq, Fr}; 6 | 7 | /* 8 | #[test] 9 | fn print_fp_for_fiat_codegen() { 10 | use ark_ff::PrimeField; 11 | use decaf377::Bls12_377; 12 | 13 | let p = ::BaseField::MODULUS; 14 | println!("p = {}", p); 15 | panic!(); 16 | } 17 | */ 18 | 19 | #[test] 20 | fn identity_encoding_is_zero() { 21 | let identity = Element::default(); 22 | let identity_bytes = identity.vartime_compress(); 23 | assert_eq!(identity_bytes.0, [0; 32]); 24 | let identity2 = identity_bytes.vartime_decompress().unwrap(); 25 | assert_eq!(identity, identity2); 26 | } 27 | 28 | #[test] 29 | fn check_generator() { 30 | let mut bytes = [0u8; 32]; 31 | 32 | for b in 1..=255 { 33 | bytes[0] = b; 34 | if let Ok(_element) = Encoding(bytes).vartime_decompress() { 35 | break; 36 | } 37 | } 38 | 39 | // The generator [8,0,...] is minimal 40 | assert_eq!(bytes[0], 8); 41 | 42 | let enc2 = Encoding(bytes) 43 | .vartime_decompress() 44 | .unwrap() 45 | .vartime_compress(); 46 | assert_eq!(bytes, enc2.0); 47 | 48 | assert_eq!( 49 | Encoding(bytes).vartime_decompress().unwrap(), 50 | Element::GENERATOR 51 | ); 52 | } 53 | 54 | #[test] 55 | fn test_encoding_matches_sage_encoding() { 56 | use hex; 57 | 58 | let mut accumulator = Element::default(); 59 | let basepoint = Element::GENERATOR; 60 | 61 | let expected_points = [ 62 | "0000000000000000000000000000000000000000000000000000000000000000", 63 | "0800000000000000000000000000000000000000000000000000000000000000", 64 | "b2ecf9b9082d6306538be73b0d6ee741141f3222152da78685d6596efc8c1506", 65 | "2ebd42dd3a2307083c834e79fb9e787e352dd33e0d719f86ae4adb02fe382409", 66 | "6acd327d70f9588fac373d165f4d9d5300510274dffdfdf2bf0955acd78da50d", 67 | "460f913e516441c286d95dd30b0a2d2bf14264f325528b06455d7cb93ba13a0b", 68 | "ec8798bcbb3bf29329549d769f89cf7993e15e2c68ec7aa2a956edf5ec62ae07", 69 | "48b01e513dd37d94c3b48940dc133b92ccba7f546e99d3fc2e602d284f609f00", 70 | "a4e85dddd19c80ecf5ef10b9d27b6626ac1a4f90bd10d263c717ecce4da6570a", 71 | "1a8fea8cbfbc91236d8c7924e3e7e617f9dd544b710ee83827737fe8dc63ae00", 72 | "0a0f86eaac0c1af30eb138467c49381edb2808904c81a4b81d2b02a2d7816006", 73 | "588125a8f4e2bab8d16affc4ca60c5f64b50d38d2bb053148021631f72e99b06", 74 | "f43f4cefbe7326eaab1584722b1b4860de554b23a14490a03f3fd63a089add0b", 75 | "76c739a33ffd15cf6554a8e705dc573f26490b64de0c5bd4e4ac75ed5af8e60b", 76 | "200136952d18d3f6c70347032ba3fef4f60c240d706be2950b4f42f1a7087705", 77 | "bcb0f922df1c7aa9579394020187a2e19e2d8073452c6ab9b0c4b052aa50f505", 78 | ]; 79 | 80 | for hexstr in expected_points { 81 | let bytes = hex::decode(hexstr).unwrap(); 82 | let point = Encoding::try_from(bytes.as_slice()) 83 | .unwrap() 84 | .vartime_decompress() 85 | .unwrap(); 86 | 87 | let result_hexstr = hex::encode(point.vartime_compress().0); 88 | 89 | assert_eq!(result_hexstr.as_str(), hexstr); 90 | 91 | assert_eq!(accumulator, point); 92 | 93 | accumulator += basepoint; 94 | } 95 | } 96 | 97 | proptest! { 98 | #[test] 99 | fn group_encoding_round_trip_if_successful(bytes: [u8; 32]) { 100 | let bytes = Encoding(bytes); 101 | 102 | if let Ok(element) = bytes.vartime_decompress() { 103 | let bytes2 = element.vartime_compress(); 104 | assert_eq!(bytes, bytes2); 105 | } 106 | } 107 | 108 | #[test] 109 | fn fq_encoding_round_trip_if_successful(bytes: [u8; 32]) { 110 | if let Ok(x) = Fq::from_bytes_checked(&bytes) { 111 | let bytes2 = x.to_bytes(); 112 | assert_eq!(bytes, bytes2); 113 | } 114 | } 115 | 116 | #[test] 117 | fn scalar_encoding_round_trip_if_successful(bytes: [u8; 32]) { 118 | if let Ok(x) = Fr::from_bytes_checked(&bytes) { 119 | let bytes2 = x.to_bytes(); 120 | assert_eq!(bytes, bytes2); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /tests/groth16_gadgets.proptest-regressions: -------------------------------------------------------------------------------- 1 | # Seeds for failure cases proptest has generated in the past. It is 2 | # automatically read and these particular cases re-run before any 3 | # novel cases are generated. 4 | # 5 | # It is recommended to check this file in to source control so that 6 | # everyone who runs the test benefits from these saved cases. 7 | cc 0d1749c1d1d2d2f48f58224507b8488724a6d2a4221bb033c05be73e3c4c1aa9 # shrinks to point = decaf377::Element(90eef1c86804abe807f403a01d585ca5006209de9f2af67cc5110214e00d940b) 8 | cc f5b5717ac6914646ebbd9597ae1735b2009631862b4a36a9b7c2f042df4c4b6a # shrinks to scalar = Fr(0x0414150b46ba2d4ca3301996c4479d0c01f51553af7cfe842a1b6fae86bb2686) 9 | cc 9eff8f94eebba5f63f45a7e5210081667dc1428c2ee40e5bc64d0aa342f9ff95 # shrinks to field_element = Fq(0x0c3c7e9ddbc9e53555bf44fa562a13592143a58b522d193767a77c2bd0d78db7) 10 | cc 54a0f66b55d1e810a5ea93e33b34c291574c94acb041a3c4d0ceb7778c3456db # shrinks to scalar_arr = [0, 0, 2, 179, 60, 20, 18, 175, 106, 150, 0, 234, 248, 82, 59, 138, 131, 219, 133, 253, 210, 6, 213, 239, 62, 54, 159, 113, 68, 188, 132, 61] 11 | cc cfdba4531a10e1fe312cc0d181a6e109e5cdbba7514035b64df13497939c5b5e # shrinks to field_element = Fq(0x1239a3f5e4408aa3333335ca45d4e48c77d0f10dd7970158b57683fffffffffd) 12 | cc 75fb114d2058a48a2bf6a719de38731fb8ee95dc113bc165c27d050f64d9aa5d # shrinks to point = decaf377::Element(c08502ce2d0d3ba9af2003bc44612f30befc606ff2a01a1cc9cff2ab85a60804) 13 | cc 44fa28495af83bd5f4a81e56cd44196d82aae58631daed033eef4b0615ccf52c # shrinks to scalar = Fr(0x00d191901856178f721cd978fb028499749dbd0aa97914e177e6fa895282140e) 14 | cc 95f7e2877390edbae420bb2f47ae30939d201597c9b67c8ac40db4f570688424 # shrinks to scalar = Fr(0x01f2b4865a52871f51a9716ab56d4365af8fc7f4fdc9b2203c75f8013446622b) 15 | cc a0a9271c37b0816ea8a7645b4f4e2e9e80a721b5c367b6841c92475d83a08cd6 # shrinks to a = decaf377::Element(34309d36c7c7d0bfcaeafd6d9057244cb51cce27ce11d2fec823bbcadd2a290d), b = decaf377::Element(262c24f6875ff501b5d69b66a29f09b131f524213a6b03ebd29fede38f72ea06) 16 | -------------------------------------------------------------------------------- /tests/operations.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use decaf377::{Element, Fq, Fr}; 4 | use proptest::prelude::*; 5 | 6 | fn element_strategy() -> BoxedStrategy { 7 | any::<[u8; 32]>() 8 | .prop_map(|bytes| Fq::from_le_bytes_mod_order(&bytes[..])) 9 | .prop_map(|r| Element::encode_to_curve(&r)) 10 | .boxed() 11 | } 12 | 13 | fn fr_strategy() -> BoxedStrategy { 14 | any::<[u8; 32]>() 15 | .prop_map(|bytes| Fr::from_le_bytes_mod_order(&bytes[..])) 16 | .boxed() 17 | } 18 | 19 | proptest! { 20 | #[test] 21 | fn scalar_mul_commutes_with_addition( 22 | a in fr_strategy(), 23 | b in fr_strategy(), 24 | P in element_strategy(), 25 | ) { 26 | assert_eq!( 27 | (a * P) + (b * P), 28 | (a + b) * P 29 | ); 30 | } 31 | 32 | #[test] 33 | fn scalar_mul_is_associative_and_commutative( 34 | a in fr_strategy(), 35 | b in fr_strategy(), 36 | P in element_strategy(), 37 | ) { 38 | assert_eq!( 39 | b * (a * P), 40 | (a * b) * P 41 | ); 42 | } 43 | 44 | #[test] 45 | fn vartime_multiscalar_mul_matches_scalar_mul( 46 | a in fr_strategy(), 47 | b in fr_strategy(), 48 | c in fr_strategy(), 49 | P in element_strategy(), 50 | Q in element_strategy(), 51 | R in element_strategy(), 52 | ) { 53 | assert_eq!( 54 | (a * P) + (b * Q) + (c * R), 55 | Element::vartime_multiscalar_mul( 56 | &[a, b, c], 57 | &[P, Q, R], 58 | ) 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /tests/test_vectors/add_assign_add_pk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/add_assign_add_pk.bin -------------------------------------------------------------------------------- /tests/test_vectors/add_assign_add_vk.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/add_assign_add_vk.param -------------------------------------------------------------------------------- /tests/test_vectors/compression_pk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/compression_pk.bin -------------------------------------------------------------------------------- /tests/test_vectors/compression_vk.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/compression_vk.param -------------------------------------------------------------------------------- /tests/test_vectors/decompression_pk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/decompression_pk.bin -------------------------------------------------------------------------------- /tests/test_vectors/decompression_vk.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/decompression_vk.param -------------------------------------------------------------------------------- /tests/test_vectors/discrete_log_pk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/discrete_log_pk.bin -------------------------------------------------------------------------------- /tests/test_vectors/discrete_log_vk.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/discrete_log_vk.param -------------------------------------------------------------------------------- /tests/test_vectors/elligator_pk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/elligator_pk.bin -------------------------------------------------------------------------------- /tests/test_vectors/elligator_vk.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/elligator_vk.param -------------------------------------------------------------------------------- /tests/test_vectors/negation_pk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/negation_pk.bin -------------------------------------------------------------------------------- /tests/test_vectors/negation_vk.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/negation_vk.param -------------------------------------------------------------------------------- /tests/test_vectors/public_element_input_pk.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/public_element_input_pk.bin -------------------------------------------------------------------------------- /tests/test_vectors/public_element_input_vk.param: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/penumbra-zone/decaf377/b75c8158dea6ab48f952e80376dec0f1eb9cab7c/tests/test_vectors/public_element_input_vk.param -------------------------------------------------------------------------------- /utils/field_properties.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | GENERATORS = { 4 | 0x01ae3a4617c510eac63b05c06ca1493b1a22d9f300f5138f1ef3622fba094800170b5d44300000008508c00000000001: 15, 5 | 0x12ab655e9a2ca55660b44d1e5c37b00159aa76fed00000010a11800000000001: 22, 6 | 0x4aad957a68b2955982d1347970dec005293a3afc43c8afeb95aee9ac33fd9ff: 5 7 | } 8 | 9 | 10 | class Properties: 11 | def __init__(self, p: int): 12 | self.p = p 13 | 14 | def one(self): 15 | return 1 16 | 17 | def modulus(self): 18 | return self.p 19 | 20 | def modulus_plus_1(self): 21 | return self.p + 1 22 | 23 | def modulus_minus_1(self): 24 | return self.p - 1 25 | 26 | def modulus_minus_5(self): 27 | return self.p - 5 28 | 29 | def modulus_bit_size(self): 30 | return self.p.bit_length() 31 | 32 | def modulus_plus_1_div_4(self): 33 | return (self.p + 1) // 4 34 | 35 | def two_adicity(self) -> int: 36 | """ 37 | Calculate the two-adicity of this field. 38 | """ 39 | acc = self.p - 1 40 | count = 0 41 | while acc & 1 == 0: 42 | count += 1 43 | acc >>= 1 44 | return count 45 | 46 | def trace(self): 47 | """ 48 | (self - 1) / 2^two_adicity 49 | """ 50 | return (self.p - 1) >> self.two_adicity() 51 | 52 | def trace_minus_one_div_two(self): 53 | return (self.trace() - 1) >> 1 54 | 55 | def mod_exp(self, a, e): 56 | insert = a 57 | acc = 1 58 | while e != 0: 59 | if e & 1 == 1: 60 | acc = acc * insert % self.p 61 | insert = (insert * insert) % self.p 62 | e = e >> 1 63 | return acc 64 | 65 | def legendre_symbol(self, x): 66 | return self.mod_exp(x, (self.p - 1) >> 1) 67 | 68 | def quadratic_non_residue(self): 69 | acc = 1 70 | while self.legendre_symbol(acc) == 1: 71 | acc += 1 72 | return acc 73 | 74 | def quadratic_non_residue_to_trace(self): 75 | return self.mod_exp(self.quadratic_non_residue(), self.trace()) 76 | 77 | def modulus_minus_one_div_two(self): 78 | return ((self.p - 1) >> 1) 79 | 80 | def generator(self): 81 | if self.p in GENERATORS: 82 | return GENERATORS[self.p] 83 | raise ValueError(f"no known generator for {hex(self.p)}") 84 | 85 | def two_adic_root_of_unity(self): 86 | return self.mod_exp(self.generator(), self.trace()) 87 | 88 | 89 | def to_le_limbs(x, size=64, expected_limb_count=0): 90 | """ 91 | Convert a number to little endian limbs, with a certain bit size. 92 | """ 93 | acc = [] 94 | while x > 0: 95 | acc.append(x & ((1 << size) - 1)) 96 | x >>= size 97 | while len(acc) < expected_limb_count: 98 | acc.append(0) 99 | return acc 100 | 101 | 102 | def to_monty(x, size, p): 103 | shift = len(to_le_limbs(p - 1, size)) * size 104 | return (x << shift) % p 105 | 106 | 107 | def main(size: int, p: int, what_to_generate: str, mode): 108 | properties = Properties(p) 109 | prop = eval(what_to_generate, locals()) 110 | expected_limb_count = ( 111 | properties.modulus_minus_1().bit_length() + size - 1) // size 112 | if not isinstance(prop, list): 113 | prop = [prop] 114 | for x in prop: 115 | if mode == "hex": 116 | print(hex(x)) 117 | elif mode == "monty": 118 | print(to_le_limbs(to_monty(x, size, p), size, expected_limb_count)) 119 | elif mode == "monty_hex": 120 | print(hex(to_monty(x, size, p))) 121 | else: 122 | print(to_le_limbs(x, size, expected_limb_count)) 123 | 124 | 125 | if __name__ == '__main__': 126 | main(int(sys.argv[1]), int(sys.argv[2], 16), 127 | sys.argv[3], sys.argv[4] if len(sys.argv) >= 5 else None) 128 | -------------------------------------------------------------------------------- /utils/frobenius_coeff_fp12_c1.txt: -------------------------------------------------------------------------------- 1 | 92949345220277864758624960506473182677953048909283248980960104381795901929519566951595905490535835115111760994353 2 | 80949648264912719408558363140637477264845294720710499478137287262712535938301461879813459410946 3 | 216465761340224619389371505802605247630151569547285782856803747159100223055385581585702401816380679166954762214499 4 | 80949648264912719408558363140637477264845294720710499478137287262712535938301461879813459410945 5 | 123516416119946754630746545296132064952198520638002533875843642777304321125866014634106496325844844051843001220146 6 | 165715080792691229252027773188420350858440463845631411558924158284924566418821255823372982649037525009328560463824 7 | 258664426012969093929703085429980814127835149614277183275038967946009968870203535512256352201271898244626862047231 8 | 42198664672744474621281227892288285906241943207628877683080515507620245292955241189266486323192680957485559243678 9 | 258664426012969093929703085429980814127835149614277183275038967946009968870203535512256352201271898244626862047232 10 | 135148009893022339379906188398761468584194992116912126664040619889416147222474808140862391813728516072597320238031 11 | -------------------------------------------------------------------------------- /utils/frobenius_coeff_fp6_c1.txt: -------------------------------------------------------------------------------- 1 | 80949648264912719408558363140637477264845294720710499478137287262712535938301461879813459410946 2 | 80949648264912719408558363140637477264845294720710499478137287262712535938301461879813459410945 3 | 258664426012969093929703085429980814127835149614277183275038967946009968870203535512256352201271898244626862047231 4 | 258664426012969093929703085429980814127835149614277183275038967946009968870203535512256352201271898244626862047232 5 | -------------------------------------------------------------------------------- /utils/frobenius_coeff_fp6_c2.txt: -------------------------------------------------------------------------------- 1 | 80949648264912719408558363140637477264845294720710499478137287262712535938301461879813459410945 2 | 258664426012969093929703085429980814127835149614277183275038967946009968870203535512256352201271898244626862047231 3 | -------------------------------------------------------------------------------- /utils/g2_generator.txt: -------------------------------------------------------------------------------- 1 | 233578398248691099356572568220835526895379068987715365179118596935057653620464273615301663571204657964920925606294 2 | 140913150380207355837477652521042157274541796891053068589147167627541651775299824604154852141315666357241556069118 3 | 63160294768292073209381361943935198908131692476676907196754037919244929611450776219210369229519898517858833747423 4 | 149157405641012693445398062341192467754805999074082136895788947234480009303640899064710353187729182149407503257491 5 | --------------------------------------------------------------------------------