├── doc ├── evidence │ ├── a │ ├── shape │ ├── rigid │ ├── d │ ├── l │ ├── p │ ├── x0 │ ├── x1 │ ├── y0 │ ├── y1 │ ├── run.sh │ ├── LICENSE │ ├── README.md │ ├── .gitignore │ └── verify.sage └── derive │ ├── .gitignore │ └── derive.sage ├── .gitignore ├── rust-toolchain.toml ├── .github ├── dependabot.yml └── workflows │ ├── lints-stable.yml │ ├── lints-beta.yml │ └── ci.yml ├── COPYRIGHT ├── tests ├── common.rs ├── fq_blackbox.rs └── fr_blackbox.rs ├── LICENSE-MIT ├── Cargo.toml ├── benches ├── fr_bench.rs ├── fq_bench.rs └── point_bench.rs ├── README.md ├── RELEASES.md ├── src ├── util.rs ├── fr.rs └── lib.rs └── LICENSE-APACHE /doc/evidence/a: -------------------------------------------------------------------------------- 1 | -1 2 | -------------------------------------------------------------------------------- /doc/evidence/shape: -------------------------------------------------------------------------------- 1 | tedwards 2 | -------------------------------------------------------------------------------- /doc/derive/.gitignore: -------------------------------------------------------------------------------- 1 | *.sage.py 2 | -------------------------------------------------------------------------------- /doc/evidence/rigid: -------------------------------------------------------------------------------- 1 | fully rigid 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /doc/evidence/d: -------------------------------------------------------------------------------- 1 | 19257038036680949359750312669786877991949435402254120286184196891950884077233 2 | -------------------------------------------------------------------------------- /doc/evidence/l: -------------------------------------------------------------------------------- 1 | 6554484396890773809930967563523245729705921265872317281365359162392183254199 2 | -------------------------------------------------------------------------------- /doc/evidence/p: -------------------------------------------------------------------------------- 1 | 52435875175126190479447740508185965837690552500527637822603658699938581184513 2 | -------------------------------------------------------------------------------- /doc/evidence/x0: -------------------------------------------------------------------------------- 1 | 11076627216317271660298050606127911965867021807910416450833192264015104452986 2 | -------------------------------------------------------------------------------- /doc/evidence/x1: -------------------------------------------------------------------------------- 1 | 8076246640662884909881801758704306714034609987455869804520522091855516602923 2 | -------------------------------------------------------------------------------- /doc/evidence/y0: -------------------------------------------------------------------------------- 1 | 44412834903739585386157632289020980010620626017712148233229312325549216099227 2 | -------------------------------------------------------------------------------- /doc/evidence/y1: -------------------------------------------------------------------------------- 1 | 13262374693698910701929044844600465831413122818447359594527400194675274060458 2 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.56.0" 3 | components = [ "clippy", "rustfmt" ] 4 | -------------------------------------------------------------------------------- /doc/evidence/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | sage verify.sage . 3 | grep -Rn '.' verify-* |grep '^verify-.*:1:' |sed 's/:1:/ = /' 4 | 5 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: github-actions 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | timezone: Etc/UTC 8 | open-pull-requests-limit: 10 9 | reviewers: 10 | - str4d 11 | assignees: 12 | - str4d 13 | labels: 14 | - "A-CI" 15 | -------------------------------------------------------------------------------- /.github/workflows/lints-stable.yml: -------------------------------------------------------------------------------- 1 | name: Stable lints 2 | 3 | # We only run these lints on trial-merges of PRs to reduce noise. 4 | on: pull_request 5 | 6 | jobs: 7 | clippy: 8 | name: Clippy (MSRV) 9 | timeout-minutes: 30 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Run clippy 14 | uses: actions-rs/clippy-check@v1 15 | with: 16 | name: Clippy (MSRV) 17 | token: ${{ secrets.GITHUB_TOKEN }} 18 | args: --all-features --all-targets -- -D warnings 19 | -------------------------------------------------------------------------------- /COPYRIGHT: -------------------------------------------------------------------------------- 1 | Copyrights in the "jubjub" library are retained by their contributors. No 2 | copyright assignment is required to contribute to the "jubjub" library. 3 | 4 | The "jubjub" library is licensed under either of 5 | 6 | * Apache License, Version 2.0, (see ./LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) 7 | * MIT license (see ./LICENSE-MIT or http://opensource.org/licenses/MIT) 8 | 9 | at your option. 10 | 11 | Unless you explicitly state otherwise, any contribution intentionally 12 | submitted for inclusion in the work by you, as defined in the Apache-2.0 13 | license, shall be dual licensed as above, without any additional terms or 14 | conditions. 15 | -------------------------------------------------------------------------------- /.github/workflows/lints-beta.yml: -------------------------------------------------------------------------------- 1 | name: Beta lints 2 | 3 | # These lints are only informative, so we only run them directly on branches 4 | # and not trial-merges of PRs, to reduce noise. 5 | on: push 6 | 7 | jobs: 8 | clippy-beta: 9 | name: Clippy (beta) 10 | timeout-minutes: 30 11 | runs-on: ubuntu-latest 12 | continue-on-error: true 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: dtolnay/rust-toolchain@beta 16 | id: toolchain 17 | - run: rustup override set ${{steps.toolchain.outputs.name}} 18 | - name: Run Clippy (beta) 19 | uses: actions-rs/clippy-check@v1 20 | continue-on-error: true 21 | with: 22 | name: Clippy (beta) 23 | token: ${{ secrets.GITHUB_TOKEN }} 24 | args: --all-features --all-targets -- -W clippy::all 25 | -------------------------------------------------------------------------------- /tests/common.rs: -------------------------------------------------------------------------------- 1 | use jubjub::*; 2 | use rand_core::{RngCore, SeedableRng}; 3 | use rand_xorshift::XorShiftRng; 4 | 5 | pub const NUM_BLACK_BOX_CHECKS: u32 = 2000; 6 | 7 | pub fn new_rng() -> XorShiftRng { 8 | XorShiftRng::from_seed([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]) 9 | } 10 | 11 | pub trait MyRandom { 12 | fn new_random(rng: &mut T) -> Self; 13 | } 14 | 15 | impl MyRandom for Fq { 16 | fn new_random(rng: &mut T) -> Self { 17 | let mut random_bytes = [0u8; 64]; 18 | rng.fill_bytes(&mut random_bytes); 19 | Fq::from_bytes_wide(&random_bytes) 20 | } 21 | } 22 | 23 | impl MyRandom for Fr { 24 | fn new_random(rng: &mut T) -> Self { 25 | let mut random_bytes = [0u8; 64]; 26 | rng.fill_bytes(&mut random_bytes); 27 | Fr::from_bytes_wide(&random_bytes) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /doc/derive/derive.sage: -------------------------------------------------------------------------------- 1 | q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 2 | Fq = GF(q) 3 | 4 | # We wish to find a Montgomery curve with B = 1 and A the smallest such 5 | # that (A - 2) / 4 is a small integer. 6 | def get_A(n): 7 | return (n * 4) + 2 8 | 9 | # A = 2 is invalid (singular curve), so we start at i = 1 (A = 6) 10 | i = 1 11 | 12 | while True: 13 | A = Fq(get_A(i)) 14 | i = i + 1 15 | 16 | # We also want that A^2 - 4 is nonsquare. 17 | if ((A^2) - 4).is_square(): 18 | continue 19 | 20 | ec = EllipticCurve(Fq, [0, A, 0, 1, 0]) 21 | o = ec.order() 22 | 23 | if (o % 8 == 0): 24 | o = o // 8 25 | if is_prime(o): 26 | twist = ec.quadratic_twist() 27 | otwist = twist.order() 28 | if (otwist % 4 == 0): 29 | otwist = otwist // 4 30 | if is_prime(otwist): 31 | print "A = %s" % A 32 | exit(0) 33 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /doc/evidence/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 The Zcash developers 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /doc/evidence/README.md: -------------------------------------------------------------------------------- 1 | Jubjub supporting evidence 2 | -------------------------- 3 | 4 | This repository contains supporting evidence that the twisted Edwards curve 5 | -x^2 + y^2 = 1 - (10240/10241).x^2.y^2 of rational points over 6 | GF(52435875175126190479447740508185965837690552500527637822603658699938581184513), 7 | [also called "Jubjub"](https://z.cash/technology/jubjub.html), 8 | satisfies the [SafeCurves criteria](https://safecurves.cr.yp.to/index.html). 9 | 10 | The script ``verify.sage`` is based on 11 | [this script from the SafeCurves site](https://safecurves.cr.yp.to/verify.html), 12 | modified 13 | 14 | * to support twisted Edwards curves; 15 | * to generate a file 'primes' containing the primes needed for primality proofs, 16 | if it is not already present; 17 | * to change the directory in which Pocklington proof files are generated 18 | (``proof/`` rather than ``../../../proof``), and to create that directory 19 | if it does not exist. 20 | 21 | Prerequisites: 22 | 23 | * apt-get install sagemath 24 | * pip install sortedcontainers 25 | 26 | Run ``sage verify.sage .``, or ``./run.sh`` to also print out the results. 27 | 28 | Note that the "rigidity" criterion cannot be checked automatically. 29 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = [ 3 | "Sean Bowe ", 4 | "Eirik Ogilvie-Wigley ", 5 | "Jack Grigg ", 6 | ] 7 | description = "Implementation of the Jubjub elliptic curve group" 8 | documentation = "https://docs.rs/jubjub/" 9 | homepage = "https://github.com/zkcrypto/jubjub" 10 | license = "MIT/Apache-2.0" 11 | name = "jubjub" 12 | repository = "https://github.com/zkcrypto/jubjub" 13 | version = "0.10.0" 14 | edition = "2021" 15 | 16 | [dependencies.bitvec] 17 | version = "1" 18 | default-features = false 19 | 20 | [dependencies.bls12_381] 21 | version = "0.8" 22 | default-features = false 23 | 24 | [dependencies.ff] 25 | version = "0.13" 26 | default-features = false 27 | 28 | [dependencies.group] 29 | version = "0.13" 30 | default-features = false 31 | 32 | [dependencies.rand_core] 33 | version = "0.6" 34 | default-features = false 35 | 36 | [dependencies.subtle] 37 | version = "^2.2.1" 38 | default-features = false 39 | 40 | [dev-dependencies] 41 | criterion = "0.3" 42 | csv = ">= 1.0, < 1.2" # csv 1.2 has MSRV 1.60 43 | 44 | [dev-dependencies.rand_xorshift] 45 | version = "0.3" 46 | default-features = false 47 | 48 | [features] 49 | default = ["alloc", "bits"] 50 | alloc = ["ff/alloc", "group/alloc"] 51 | bits = ["ff/bits"] 52 | 53 | [[bench]] 54 | name = "fq_bench" 55 | harness = false 56 | 57 | [[bench]] 58 | name = "fr_bench" 59 | harness = false 60 | 61 | [[bench]] 62 | name = "point_bench" 63 | harness = false 64 | -------------------------------------------------------------------------------- /benches/fr_bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use jubjub::*; 3 | 4 | fn bench_add_assign(c: &mut Criterion) { 5 | let mut n = Fr::one(); 6 | let neg_one = -Fr::one(); 7 | c.bench_function("Fr add_assign", |b| { 8 | b.iter(move || { 9 | n += &neg_one; 10 | }) 11 | }); 12 | } 13 | 14 | fn bench_sub_assign(c: &mut Criterion) { 15 | let mut n = Fr::one(); 16 | let neg_one = -Fr::one(); 17 | c.bench_function("Fr sub_assign", |b| { 18 | b.iter(move || { 19 | n -= &neg_one; 20 | }) 21 | }); 22 | } 23 | 24 | fn bench_mul_assign(c: &mut Criterion) { 25 | let mut n = Fr::one(); 26 | let neg_one = -Fr::one(); 27 | c.bench_function("Fr mul_assign", |b| { 28 | b.iter(move || { 29 | n *= &neg_one; 30 | }) 31 | }); 32 | } 33 | 34 | fn bench_square(c: &mut Criterion) { 35 | let n = Fr::one(); 36 | c.bench_function("Fr square", |b| b.iter(move || n.square())); 37 | } 38 | 39 | fn bench_invert(c: &mut Criterion) { 40 | let n = Fr::one(); 41 | c.bench_function("Fr invert", |b| b.iter(move || n.invert())); 42 | } 43 | 44 | fn bench_sqrt(c: &mut Criterion) { 45 | let n = Fr::one().double().double(); 46 | c.bench_function("Fr sqrt", |b| b.iter(move || n.sqrt())); 47 | } 48 | 49 | criterion_group!( 50 | benches, 51 | bench_add_assign, 52 | bench_sub_assign, 53 | bench_mul_assign, 54 | bench_square, 55 | bench_invert, 56 | bench_sqrt, 57 | ); 58 | criterion_main!(benches); 59 | -------------------------------------------------------------------------------- /benches/fq_bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use ff::Field; 3 | use jubjub::*; 4 | 5 | fn bench_add_assign(c: &mut Criterion) { 6 | let mut n = Fq::one(); 7 | let neg_one = -Fq::one(); 8 | c.bench_function("Fq add_assign", |b| { 9 | b.iter(move || { 10 | n += &neg_one; 11 | }) 12 | }); 13 | } 14 | 15 | fn bench_sub_assign(c: &mut Criterion) { 16 | let mut n = Fq::one(); 17 | let neg_one = -Fq::one(); 18 | c.bench_function("Fq sub_assign", |b| { 19 | b.iter(move || { 20 | n -= &neg_one; 21 | }) 22 | }); 23 | } 24 | 25 | fn bench_mul_assign(c: &mut Criterion) { 26 | let mut n = Fq::one(); 27 | let neg_one = -Fq::one(); 28 | c.bench_function("Fq mul_assign", |b| { 29 | b.iter(move || { 30 | n *= &neg_one; 31 | }) 32 | }); 33 | } 34 | 35 | fn bench_square(c: &mut Criterion) { 36 | let n = Fq::one(); 37 | c.bench_function("Fq square", |b| b.iter(move || n.square())); 38 | } 39 | 40 | fn bench_invert(c: &mut Criterion) { 41 | let n = Fq::one(); 42 | c.bench_function("Fq invert", |b| b.iter(move || n.invert())); 43 | } 44 | 45 | fn bench_sqrt(c: &mut Criterion) { 46 | let n = Fq::one().double().double(); 47 | c.bench_function("Fq sqrt", |b| b.iter(move || n.sqrt())); 48 | } 49 | 50 | criterion_group!( 51 | benches, 52 | bench_add_assign, 53 | bench_sub_assign, 54 | bench_mul_assign, 55 | bench_square, 56 | bench_invert, 57 | bench_sqrt, 58 | ); 59 | criterion_main!(benches); 60 | -------------------------------------------------------------------------------- /doc/evidence/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI checks 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | test: 7 | name: Test on ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [ubuntu-latest, windows-latest, macOS-latest] 12 | 13 | steps: 14 | - uses: actions/checkout@v3 15 | - name: Run tests 16 | run: cargo test --verbose --release 17 | 18 | no-std: 19 | name: Check no-std target ${{ matrix.target }} 20 | runs-on: ubuntu-latest 21 | strategy: 22 | matrix: 23 | target: 24 | - thumbv6m-none-eabi 25 | - wasm32-unknown-unknown 26 | - wasm32-wasi 27 | 28 | steps: 29 | - uses: actions/checkout@v3 30 | - run: rustup target add ${{ matrix.target }} 31 | - run: cargo fetch 32 | - name: Build 33 | run: > 34 | cargo build 35 | --verbose 36 | --target ${{ matrix.target }} 37 | --no-default-features 38 | 39 | bitrot: 40 | name: Bitrot check 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v3 44 | # Build benchmarks and all-features to prevent bitrot 45 | - name: Build benchmarks 46 | run: cargo build --benches --examples --all-features 47 | 48 | doc-links: 49 | name: Intra-doc links 50 | runs-on: ubuntu-latest 51 | steps: 52 | - uses: actions/checkout@v3 53 | - run: cargo fetch 54 | # Requires #![deny(rustdoc::broken_intra_doc_links)] in crate. 55 | - name: Check intra-doc links 56 | run: cargo doc --document-private-items 57 | 58 | fmt: 59 | name: Rustfmt 60 | timeout-minutes: 30 61 | runs-on: ubuntu-latest 62 | steps: 63 | - uses: actions/checkout@v3 64 | - name: Check formatting 65 | run: cargo fmt --all -- --check 66 | -------------------------------------------------------------------------------- /benches/point_bench.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, Criterion}; 2 | use jubjub::*; 3 | 4 | // Non-Niels 5 | 6 | fn bench_point_doubling(c: &mut Criterion) { 7 | let a = ExtendedPoint::identity(); 8 | c.bench_function("Jubjub point doubling", |bencher| { 9 | bencher.iter(move || a.double()) 10 | }); 11 | } 12 | 13 | fn bench_point_addition(c: &mut Criterion) { 14 | let a = ExtendedPoint::identity(); 15 | let b = -ExtendedPoint::identity(); 16 | c.bench_function("Jubjub point addition", |bencher| { 17 | bencher.iter(move || a + b) 18 | }); 19 | } 20 | 21 | fn bench_point_subtraction(c: &mut Criterion) { 22 | let a = ExtendedPoint::identity(); 23 | let b = -ExtendedPoint::identity(); 24 | c.bench_function("Jubjub point subtraction", |bencher| { 25 | bencher.iter(move || a + b) 26 | }); 27 | } 28 | 29 | // Niels 30 | 31 | fn bench_cached_point_addition(c: &mut Criterion) { 32 | let a = ExtendedPoint::identity(); 33 | let b = ExtendedPoint::identity().to_niels(); 34 | c.bench_function("Jubjub cached point addition", |bencher| { 35 | bencher.iter(move || a + b) 36 | }); 37 | } 38 | 39 | fn bench_cached_point_subtraction(c: &mut Criterion) { 40 | let a = ExtendedPoint::identity(); 41 | let b = ExtendedPoint::identity().to_niels(); 42 | c.bench_function("Jubjub cached point subtraction", |bencher| { 43 | bencher.iter(move || a + b) 44 | }); 45 | } 46 | 47 | fn bench_cached_affine_point_addition(c: &mut Criterion) { 48 | let a = ExtendedPoint::identity(); 49 | let b = AffinePoint::identity().to_niels(); 50 | c.bench_function("Jubjub cached affine point addition", |bencher| { 51 | bencher.iter(move || a + b) 52 | }); 53 | } 54 | 55 | fn bench_cached_affine_point_subtraction(c: &mut Criterion) { 56 | let a = ExtendedPoint::identity(); 57 | let b = AffinePoint::identity().to_niels(); 58 | c.bench_function("Jubjub cached affine point subtraction", |bencher| { 59 | bencher.iter(move || a + b) 60 | }); 61 | } 62 | 63 | criterion_group!( 64 | benches, 65 | bench_point_doubling, 66 | bench_point_addition, 67 | bench_point_subtraction, 68 | bench_cached_point_addition, 69 | bench_cached_point_subtraction, 70 | bench_cached_affine_point_addition, 71 | bench_cached_affine_point_subtraction, 72 | ); 73 | criterion_main!(benches); 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jubjub [![Crates.io](https://img.shields.io/crates/v/jubjub.svg)](https://crates.io/crates/jubjub) # 2 | 3 | 7 | 8 | This is a pure Rust implementation of the Jubjub elliptic curve group and its associated fields. 9 | 10 | * **This implementation has not been reviewed or audited. Use at your own risk.** 11 | * This implementation targets Rust `1.56` or later. 12 | * All operations are constant time unless explicitly noted. 13 | * This implementation does not require the Rust standard library. 14 | 15 | ## RFC process 16 | 17 | This crate follows the [zkcrypto RFC process](https://zkcrypto.github.io/rfcs/). 18 | If you want to propose "substantial" changes to this crate, please 19 | [create an RFC](https://github.com/zkcrypto/rfcs) for wider discussion. 20 | 21 | ## [Documentation](https://docs.rs/jubjub) 22 | 23 | ## Curve Description 24 | 25 | Jubjub is the [twisted Edwards curve](https://en.wikipedia.org/wiki/Twisted_Edwards_curve) `-u^2 + v^2 = 1 + d.u^2.v^2` of rational points over `GF(q)` with a subgroup of prime order `r` and cofactor `8`. 26 | 27 | ``` 28 | q = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001 29 | r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 30 | d = -(10240/10241) 31 | ``` 32 | 33 | The choice of `GF(q)` is made to be the scalar field of the BLS12-381 elliptic curve construction. 34 | 35 | Jubjub is birationally equivalent to a [Montgomery curve](https://en.wikipedia.org/wiki/Montgomery_curve) `y^2 = x^3 + Ax^2 + x` over the same field with `A = 40962`. This value of `A` is the smallest integer such that `(A - 2) / 4` is a small integer, `A^2 - 4` is nonsquare in `GF(q)`, and the Montgomery curve and its quadratic twist have small cofactors `8` and `4`, respectively. This is identical to the relationship between Curve25519 and ed25519. 36 | 37 | Please see [./doc/evidence/](./doc/evidence/) for supporting evidence that Jubjub meets the [SafeCurves](https://safecurves.cr.yp.to/index.html) criteria. The tool in [./doc/derive/](./doc/derive/) will derive the curve parameters via the above criteria to demonstrate rigidity. 38 | 39 | ## Acknowledgements 40 | 41 | Jubjub was designed by Sean Bowe. Daira Hopwood is responsible for its name and specification. The security evidence in [./doc/evidence/](./doc/evidence/) is the product of Daira Hopwood and based on SafeCurves by Daniel J. Bernstein and Tanja Lange. Peter Newell and Daira Hopwood are responsible for the Jubjub bird image. 42 | 43 | Please see `Cargo.toml` for a list of primary authors of this codebase. 44 | 45 | ## License 46 | 47 | Licensed under either of 48 | 49 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 50 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 51 | 52 | at your option. 53 | 54 | ### Contribution 55 | 56 | Unless you explicitly state otherwise, any contribution intentionally 57 | submitted for inclusion in the work by you, as defined in the Apache-2.0 58 | license, shall be dual licensed as above, without any additional terms or 59 | conditions. 60 | -------------------------------------------------------------------------------- /tests/fq_blackbox.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; 4 | use jubjub::*; 5 | 6 | #[test] 7 | fn test_to_and_from_bytes() { 8 | let mut rng = new_rng(); 9 | for _ in 0..NUM_BLACK_BOX_CHECKS { 10 | let a = Fq::new_random(&mut rng); 11 | assert_eq!(a, Fq::from_bytes(&Fq::to_bytes(&a)).unwrap()); 12 | } 13 | } 14 | 15 | #[test] 16 | fn test_additive_associativity() { 17 | let mut rng = new_rng(); 18 | for _ in 0..NUM_BLACK_BOX_CHECKS { 19 | let a = Fq::new_random(&mut rng); 20 | let b = Fq::new_random(&mut rng); 21 | let c = Fq::new_random(&mut rng); 22 | assert_eq!((a + b) + c, a + (b + c)) 23 | } 24 | } 25 | 26 | #[test] 27 | fn test_additive_identity() { 28 | let mut rng = new_rng(); 29 | for _ in 0..NUM_BLACK_BOX_CHECKS { 30 | let a = Fq::new_random(&mut rng); 31 | assert_eq!(a, a + Fq::zero()); 32 | assert_eq!(a, Fq::zero() + a); 33 | } 34 | } 35 | 36 | #[test] 37 | fn test_subtract_additive_identity() { 38 | let mut rng = new_rng(); 39 | for _ in 0..NUM_BLACK_BOX_CHECKS { 40 | let a = Fq::new_random(&mut rng); 41 | assert_eq!(a, a - Fq::zero()); 42 | assert_eq!(a, Fq::zero() - -&a); 43 | } 44 | } 45 | 46 | #[test] 47 | fn test_additive_inverse() { 48 | let mut rng = new_rng(); 49 | for _ in 0..NUM_BLACK_BOX_CHECKS { 50 | let a = Fq::new_random(&mut rng); 51 | let a_neg = -&a; 52 | assert_eq!(Fq::zero(), a + a_neg); 53 | assert_eq!(Fq::zero(), a_neg + a); 54 | } 55 | } 56 | 57 | #[allow(clippy::eq_op)] 58 | #[test] 59 | fn test_additive_commutativity() { 60 | let mut rng = new_rng(); 61 | for _ in 0..NUM_BLACK_BOX_CHECKS { 62 | let a = Fq::new_random(&mut rng); 63 | let b = Fq::new_random(&mut rng); 64 | assert_eq!(a + b, b + a); 65 | } 66 | } 67 | 68 | #[test] 69 | fn test_multiplicative_associativity() { 70 | let mut rng = new_rng(); 71 | for _ in 0..NUM_BLACK_BOX_CHECKS { 72 | let a = Fq::new_random(&mut rng); 73 | let b = Fq::new_random(&mut rng); 74 | let c = Fq::new_random(&mut rng); 75 | assert_eq!((a * b) * c, a * (b * c)) 76 | } 77 | } 78 | 79 | #[test] 80 | fn test_multiplicative_identity() { 81 | let mut rng = new_rng(); 82 | for _ in 0..NUM_BLACK_BOX_CHECKS { 83 | let a = Fq::new_random(&mut rng); 84 | assert_eq!(a, a * Fq::one()); 85 | assert_eq!(a, Fq::one() * a); 86 | } 87 | } 88 | 89 | #[test] 90 | fn test_multiplicative_inverse() { 91 | let mut rng = new_rng(); 92 | for _ in 0..NUM_BLACK_BOX_CHECKS { 93 | let a = Fq::new_random(&mut rng); 94 | if a == Fq::zero() { 95 | continue; 96 | } 97 | let a_inv = a.invert().unwrap(); 98 | assert_eq!(Fq::one(), a * a_inv); 99 | assert_eq!(Fq::one(), a_inv * a); 100 | } 101 | } 102 | 103 | #[test] 104 | fn test_multiplicative_commutativity() { 105 | let mut rng = new_rng(); 106 | for _ in 0..NUM_BLACK_BOX_CHECKS { 107 | let a = Fq::new_random(&mut rng); 108 | let b = Fq::new_random(&mut rng); 109 | assert_eq!(a * b, b * a); 110 | } 111 | } 112 | 113 | #[test] 114 | fn test_multiply_additive_identity() { 115 | let mut rng = new_rng(); 116 | for _ in 0..NUM_BLACK_BOX_CHECKS { 117 | let a = Fq::new_random(&mut rng); 118 | assert_eq!(Fq::zero(), Fq::zero() * a); 119 | assert_eq!(Fq::zero(), a * Fq::zero()); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /tests/fr_blackbox.rs: -------------------------------------------------------------------------------- 1 | mod common; 2 | 3 | use common::{new_rng, MyRandom, NUM_BLACK_BOX_CHECKS}; 4 | use jubjub::*; 5 | 6 | #[test] 7 | fn test_to_and_from_bytes() { 8 | let mut rng = new_rng(); 9 | for _ in 0..NUM_BLACK_BOX_CHECKS { 10 | let a = Fr::new_random(&mut rng); 11 | assert_eq!(a, Fr::from_bytes(&Fr::to_bytes(&a)).unwrap()); 12 | } 13 | } 14 | 15 | #[test] 16 | fn test_additive_associativity() { 17 | let mut rng = new_rng(); 18 | for _ in 0..NUM_BLACK_BOX_CHECKS { 19 | let a = Fr::new_random(&mut rng); 20 | let b = Fr::new_random(&mut rng); 21 | let c = Fr::new_random(&mut rng); 22 | assert_eq!((a + b) + c, a + (b + c)) 23 | } 24 | } 25 | 26 | #[test] 27 | fn test_additive_identity() { 28 | let mut rng = new_rng(); 29 | for _ in 0..NUM_BLACK_BOX_CHECKS { 30 | let a = Fr::new_random(&mut rng); 31 | assert_eq!(a, a + Fr::zero()); 32 | assert_eq!(a, Fr::zero() + a); 33 | } 34 | } 35 | 36 | #[test] 37 | fn test_subtract_additive_identity() { 38 | let mut rng = new_rng(); 39 | for _ in 0..NUM_BLACK_BOX_CHECKS { 40 | let a = Fr::new_random(&mut rng); 41 | assert_eq!(a, a - Fr::zero()); 42 | assert_eq!(a, Fr::zero() - -&a); 43 | } 44 | } 45 | 46 | #[test] 47 | fn test_additive_inverse() { 48 | let mut rng = new_rng(); 49 | for _ in 0..NUM_BLACK_BOX_CHECKS { 50 | let a = Fr::new_random(&mut rng); 51 | let a_neg = -&a; 52 | assert_eq!(Fr::zero(), a + a_neg); 53 | assert_eq!(Fr::zero(), a_neg + a); 54 | } 55 | } 56 | 57 | #[allow(clippy::eq_op)] 58 | #[test] 59 | fn test_additive_commutativity() { 60 | let mut rng = new_rng(); 61 | for _ in 0..NUM_BLACK_BOX_CHECKS { 62 | let a = Fr::new_random(&mut rng); 63 | let b = Fr::new_random(&mut rng); 64 | assert_eq!(a + b, b + a); 65 | } 66 | } 67 | 68 | #[test] 69 | fn test_multiplicative_associativity() { 70 | let mut rng = new_rng(); 71 | for _ in 0..NUM_BLACK_BOX_CHECKS { 72 | let a = Fr::new_random(&mut rng); 73 | let b = Fr::new_random(&mut rng); 74 | let c = Fr::new_random(&mut rng); 75 | assert_eq!((a * b) * c, a * (b * c)) 76 | } 77 | } 78 | 79 | #[test] 80 | fn test_multiplicative_identity() { 81 | let mut rng = new_rng(); 82 | for _ in 0..NUM_BLACK_BOX_CHECKS { 83 | let a = Fr::new_random(&mut rng); 84 | assert_eq!(a, a * Fr::one()); 85 | assert_eq!(a, Fr::one() * a); 86 | } 87 | } 88 | 89 | #[test] 90 | fn test_multiplicative_inverse() { 91 | let mut rng = new_rng(); 92 | for _ in 0..NUM_BLACK_BOX_CHECKS { 93 | let a = Fr::new_random(&mut rng); 94 | if a == Fr::zero() { 95 | continue; 96 | } 97 | let a_inv = a.invert().unwrap(); 98 | assert_eq!(Fr::one(), a * a_inv); 99 | assert_eq!(Fr::one(), a_inv * a); 100 | } 101 | } 102 | 103 | #[test] 104 | fn test_multiplicative_commutativity() { 105 | let mut rng = new_rng(); 106 | for _ in 0..NUM_BLACK_BOX_CHECKS { 107 | let a = Fr::new_random(&mut rng); 108 | let b = Fr::new_random(&mut rng); 109 | assert_eq!(a * b, b * a); 110 | } 111 | } 112 | 113 | #[test] 114 | fn test_multiply_additive_identity() { 115 | let mut rng = new_rng(); 116 | for _ in 0..NUM_BLACK_BOX_CHECKS { 117 | let a = Fr::new_random(&mut rng); 118 | assert_eq!(Fr::zero(), Fr::zero() * a); 119 | assert_eq!(Fr::zero(), a * Fr::zero()); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /RELEASES.md: -------------------------------------------------------------------------------- 1 | # 0.10.0 2 | ## Changed 3 | - Bumped dependencies to `bls12_381 0.8`, `ff 0.13`, `group 0.13`. 4 | 5 | # 0.9.0 6 | 7 | ## Changed 8 | - Bumped MSRV to `1.56.0` 9 | - Bumped dependencies to `bls12_381 0.7`, `ff 0.12`, `group 0.12`, `bitvec 1.0`. 10 | 11 | # 0.8.0 12 | ## Added 13 | - `jubjub::Base`, as an alias for `jubjub::Fq`. 14 | - `jubjub::AffinePoint::batch_from_bytes`, which enables the inversion inside 15 | `jubjub::AffinePoint::from_bytes` to be batched. 16 | 17 | ## Changed 18 | - Bumped dependencies to `bls12_381 0.6`, `ff 0.11`, `group 0.11`. 19 | 20 | # 0.7.0 21 | ## Security 22 | - A bug in the `jubjub::{AffinePoint, ExtendedPoint, SubgroupPoint}::from_bytes` 23 | APIs (and their `group::GroupEncoding` implementations) has been fixed. The 24 | APIs were documented as rejecting non-canonical points, but were accidentally 25 | accepting two specific non-canonical encodings. This could potentially cause a 26 | problem in consensus-critical protocols that expect encodings to be round-trip 27 | compatible (i.e. `AffinePoint::from_bytes(b).unwrap().to_bytes() == b`). See 28 | [ZIP 216](https://zips.z.cash/zip-0216) for more details. 29 | - A new API `jubjub::AffinePoint::from_bytes_pre_zip216_compatibility` 30 | preserves the previous behaviour, for use where consensus compatibility is 31 | required. 32 | 33 | ## Changed 34 | - Bumped dependencies to `bitvec 0.22`, `bls12_381 0.5`, `ff 0.10`, 35 | `group 0.10`. 36 | - MSRV is now 1.51.0. 37 | 38 | # 0.6.0 39 | ## Changed 40 | - Bumped dependencies to `bitvec 0.20`, `bls12_381 0.4`, `ff 0.9`, `group 0.9`, 41 | `rand_core 0.6`. 42 | - MSRV is now 1.47.0. 43 | 44 | # 0.5.1 45 | 46 | # Fixed 47 | * The crate now compiles for non-64-bit targets, such as the `wasm32-*` targets. 48 | 49 | # 0.5.0 50 | 51 | This upgrade bumps our dependencies `bls12_381`, `group` and `ff`, while making 52 | corresponding changes to the APIs. This release now only supports Rust compilers 53 | version 1.44.0 or later. 54 | 55 | # 0.4.0 56 | 57 | This release adds implementations of the `ff` and `group` traits. Additional trait 58 | implementations (for standard traits) have been added where the `ff` and `group` trait 59 | bounds require them. 60 | 61 | ## Added 62 | * `jubjub::SubgroupPoint`, which represents an element of Jubjub's prime-order subgroup. 63 | It implements the following traits: 64 | * `group::{Group, GroupEncoding}` 65 | * `group::prime::PrimeGroup` 66 | * New trait implementations for `jubjub::ExtendedPoint`: 67 | * `group::{Curve, Group, GroupEncoding, WnafGroup}` 68 | * `group::cofactor::{CofactorCurve, CofactorGroup}` 69 | * New trait implementations for `jubjub::AffinePoint`: 70 | * `group::GroupEncoding` 71 | * `group::cofactor::CofactorCurveAffine` 72 | * New trait implementations for `jubjub::Fr`: 73 | * `ff::{Field, PrimeField}` 74 | * `jubjub::AffinePoint::is_identity` 75 | * `jubjub::AffinePoint::to_extended` 76 | * `jubjub::Scalar`, as an alias for `jubjub::Fr`. 77 | 78 | ## Changed 79 | * We've migrated to `bls12_381 0.2`. 80 | * `rand_core` is now a regular dependency. 81 | * We depend on the `byteorder` crate again, as it is part of the `ff::PrimeField` trait. 82 | * The benchmarks are now implemented using `criterion`. 83 | 84 | # 0.3.0 85 | 86 | This release now depends on the `bls12_381` crate, which exposes the `Fq` field type that we re-export. 87 | 88 | * The `Fq` and `Fr` field types now have better constant function support for various operations and constructors. 89 | * We no longer depend on the `byteorder` crate. 90 | * We've bumped our `rand_core` dev-dependency up to 0.5. 91 | * We've removed the `std` and `nightly` features. 92 | * We've bumped our dependency of `subtle` up to `^2.2.1`. 93 | 94 | # 0.2.0 95 | 96 | This release switches to `subtle 2.1` to bring in the `CtOption` type, and also makes a few useful API changes. 97 | 98 | * Implemented `Mul` for `AffineNielsPoint` and `ExtendedNielsPoint` 99 | * Changed `AffinePoint::to_niels()` to be a `const` function so that constant curve points can be constructed without statics. 100 | * Implemented `multiply_bits` for `AffineNielsPoint`, `ExtendedNielsPoint` 101 | * Removed `CtOption` and replaced it with `CtOption` from `subtle` crate. 102 | * Modified receivers of some methods to reduce stack usage 103 | * Changed various `into_bytes` methods into `to_bytes` 104 | 105 | # 0.1.0 106 | 107 | Initial release. 108 | -------------------------------------------------------------------------------- /src/util.rs: -------------------------------------------------------------------------------- 1 | /// Compute a + b + carry, returning the result and the new carry over. 2 | #[inline(always)] 3 | pub const fn adc(a: u64, b: u64, carry: u64) -> (u64, u64) { 4 | let ret = (a as u128) + (b as u128) + (carry as u128); 5 | (ret as u64, (ret >> 64) as u64) 6 | } 7 | 8 | /// Compute a - (b + borrow), returning the result and the new borrow. 9 | #[inline(always)] 10 | pub const fn sbb(a: u64, b: u64, borrow: u64) -> (u64, u64) { 11 | let ret = (a as u128).wrapping_sub((b as u128) + ((borrow >> 63) as u128)); 12 | (ret as u64, (ret >> 64) as u64) 13 | } 14 | 15 | /// Compute a + (b * c) + carry, returning the result and the new carry over. 16 | #[inline(always)] 17 | pub const fn mac(a: u64, b: u64, c: u64, carry: u64) -> (u64, u64) { 18 | let ret = (a as u128) + ((b as u128) * (c as u128)) + (carry as u128); 19 | (ret as u64, (ret >> 64) as u64) 20 | } 21 | 22 | macro_rules! impl_add_binop_specify_output { 23 | ($lhs:ident, $rhs:ident, $output:ident) => { 24 | impl<'b> Add<&'b $rhs> for $lhs { 25 | type Output = $output; 26 | 27 | #[inline] 28 | fn add(self, rhs: &'b $rhs) -> $output { 29 | &self + rhs 30 | } 31 | } 32 | 33 | impl<'a> Add<$rhs> for &'a $lhs { 34 | type Output = $output; 35 | 36 | #[inline] 37 | fn add(self, rhs: $rhs) -> $output { 38 | self + &rhs 39 | } 40 | } 41 | 42 | impl Add<$rhs> for $lhs { 43 | type Output = $output; 44 | 45 | #[inline] 46 | fn add(self, rhs: $rhs) -> $output { 47 | &self + &rhs 48 | } 49 | } 50 | }; 51 | } 52 | 53 | macro_rules! impl_sub_binop_specify_output { 54 | ($lhs:ident, $rhs:ident, $output:ident) => { 55 | impl<'b> Sub<&'b $rhs> for $lhs { 56 | type Output = $output; 57 | 58 | #[inline] 59 | fn sub(self, rhs: &'b $rhs) -> $output { 60 | &self - rhs 61 | } 62 | } 63 | 64 | impl<'a> Sub<$rhs> for &'a $lhs { 65 | type Output = $output; 66 | 67 | #[inline] 68 | fn sub(self, rhs: $rhs) -> $output { 69 | self - &rhs 70 | } 71 | } 72 | 73 | impl Sub<$rhs> for $lhs { 74 | type Output = $output; 75 | 76 | #[inline] 77 | fn sub(self, rhs: $rhs) -> $output { 78 | &self - &rhs 79 | } 80 | } 81 | }; 82 | } 83 | 84 | macro_rules! impl_binops_additive_specify_output { 85 | ($lhs:ident, $rhs:ident, $output:ident) => { 86 | impl_add_binop_specify_output!($lhs, $rhs, $output); 87 | impl_sub_binop_specify_output!($lhs, $rhs, $output); 88 | }; 89 | } 90 | 91 | macro_rules! impl_binops_multiplicative_mixed { 92 | ($lhs:ident, $rhs:ident, $output:ident) => { 93 | impl<'b> Mul<&'b $rhs> for $lhs { 94 | type Output = $output; 95 | 96 | #[inline] 97 | fn mul(self, rhs: &'b $rhs) -> $output { 98 | &self * rhs 99 | } 100 | } 101 | 102 | impl<'a> Mul<$rhs> for &'a $lhs { 103 | type Output = $output; 104 | 105 | #[inline] 106 | fn mul(self, rhs: $rhs) -> $output { 107 | self * &rhs 108 | } 109 | } 110 | 111 | impl Mul<$rhs> for $lhs { 112 | type Output = $output; 113 | 114 | #[inline] 115 | fn mul(self, rhs: $rhs) -> $output { 116 | &self * &rhs 117 | } 118 | } 119 | }; 120 | } 121 | 122 | macro_rules! impl_binops_additive { 123 | ($lhs:ident, $rhs:ident) => { 124 | impl_binops_additive_specify_output!($lhs, $rhs, $lhs); 125 | 126 | impl SubAssign<$rhs> for $lhs { 127 | #[inline] 128 | fn sub_assign(&mut self, rhs: $rhs) { 129 | *self = &*self - &rhs; 130 | } 131 | } 132 | 133 | impl AddAssign<$rhs> for $lhs { 134 | #[inline] 135 | fn add_assign(&mut self, rhs: $rhs) { 136 | *self = &*self + &rhs; 137 | } 138 | } 139 | 140 | impl<'b> SubAssign<&'b $rhs> for $lhs { 141 | #[inline] 142 | fn sub_assign(&mut self, rhs: &'b $rhs) { 143 | *self = &*self - rhs; 144 | } 145 | } 146 | 147 | impl<'b> AddAssign<&'b $rhs> for $lhs { 148 | #[inline] 149 | fn add_assign(&mut self, rhs: &'b $rhs) { 150 | *self = &*self + rhs; 151 | } 152 | } 153 | }; 154 | } 155 | 156 | macro_rules! impl_binops_multiplicative { 157 | ($lhs:ident, $rhs:ident) => { 158 | impl_binops_multiplicative_mixed!($lhs, $rhs, $lhs); 159 | 160 | impl MulAssign<$rhs> for $lhs { 161 | #[inline] 162 | fn mul_assign(&mut self, rhs: $rhs) { 163 | *self = &*self * &rhs; 164 | } 165 | } 166 | 167 | impl<'b> MulAssign<&'b $rhs> for $lhs { 168 | #[inline] 169 | fn mul_assign(&mut self, rhs: &'b $rhs) { 170 | *self = &*self * rhs; 171 | } 172 | } 173 | }; 174 | } 175 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /doc/evidence/verify.sage: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | from errno import ENOENT, EEXIST 4 | from sortedcontainers import SortedSet 5 | 6 | 7 | def readfile(fn): 8 | fd = open(fn,'r') 9 | r = fd.read() 10 | fd.close() 11 | return r 12 | 13 | def writefile(fn,s): 14 | fd = open(fn,'w') 15 | fd.write(s) 16 | fd.close() 17 | 18 | def expand2(n): 19 | s = "" 20 | 21 | while n != 0: 22 | j = 16 23 | while 2**j < abs(n): j += 1 24 | if 2**j - abs(n) > abs(n) - 2**(j-1): j -= 1 25 | 26 | if abs(abs(n) - 2**j) > 2**(j - 10): 27 | if n > 0: 28 | if s != "": s += " + " 29 | s += str(n) 30 | else: 31 | s += " - " + str(-n) 32 | n = 0 33 | elif n > 0: 34 | if s != "": s += " + " 35 | s += "2^" + str(j) 36 | n -= 2**j 37 | else: 38 | s += " - 2^" + str(j) 39 | n += 2**j 40 | 41 | return s 42 | 43 | def requirement(fn,istrue): 44 | writefile(fn,str(istrue) + '\n') 45 | return istrue 46 | 47 | def verify(): 48 | try: 49 | os.mkdir('proof') 50 | except OSError as e: 51 | if e.errno != EEXIST: raise 52 | 53 | try: 54 | s = set(map(Integer, readfile('primes').split())) 55 | except IOError, e: 56 | if e.errno != ENOENT: raise 57 | s = set() 58 | 59 | needtofactor = SortedSet() 60 | V = SortedSet() # distinct verified primes 61 | verify_primes(V, s, needtofactor) 62 | verify_pass(V, needtofactor) 63 | 64 | old = V 65 | needtofactor.update(V) 66 | while len(needtofactor) > len(old): 67 | k = len(needtofactor) - len(old) 68 | sys.stdout.write('Factoring %d integer%s' % (k, '' if k == 1 else 's')) 69 | sys.stdout.flush() 70 | for x in needtofactor: 71 | if x not in old: 72 | for (y, z) in factor(x): 73 | s.add(y) 74 | sys.stdout.write('.') 75 | sys.stdout.flush() 76 | 77 | print('') 78 | 79 | old = needtofactor.copy() 80 | verify_primes(V, s, needtofactor) 81 | 82 | writefile('primes', '\n'.join(map(str, s)) + '\n') 83 | writefile('verify-primes', '\n' + 84 | ''.join(('2\n' if v == 2 else 85 | '%s\n' % (v,v)) for v in V) + 86 | '\n') 87 | 88 | verify_pass(V, needtofactor) 89 | 90 | 91 | def verify_primes(V, s, needtofactor): 92 | for n in sorted(s): 93 | if not n.is_prime() or n in V: continue 94 | needtofactor.add(n-1) 95 | if n == 2: 96 | V.add(n) 97 | continue 98 | for trybase in primes(2,10000): 99 | base = Integers(n)(trybase) 100 | if base^(n-1) != 1: continue 101 | proof = 'Primality proof for n = %s:\n' % n 102 | proof += '

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

b^(n-1) mod n = 1.\n' 104 | f = factor(1) 105 | for v in reversed(V): 106 | if f.prod()^2 <= n: 107 | if n % v == 1: 108 | u = base^((n-1)/v)-1 109 | if u.is_unit(): 110 | if v == 2: 111 | proof += '

2 is prime.\n' 112 | else: 113 | proof += '

%s is prime.\n' % (v,v) 114 | proof += '
b^((n-1)/%s)-1 mod n = %s, which is a unit, inverse %s.\n' % (v,u,1/u) 115 | f *= factor(v)^(n-1).valuation(v) 116 | if f.prod()^2 <= n: continue 117 | if n % f.prod() != 1: continue 118 | proof += '

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

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

n is prime by Pocklington's theorem.\n" 121 | proof += '\n' 122 | writefile('proof/%s.html' % n,proof) 123 | V.add(n) 124 | break 125 | 126 | 127 | def verify_pass(V, needtofactor): 128 | p = Integer(readfile('p')) 129 | k = GF(p) 130 | kz. = k[] 131 | l = Integer(readfile('l')) 132 | x0 = Integer(readfile('x0')) 133 | y0 = Integer(readfile('y0')) 134 | x1 = Integer(readfile('x1')) 135 | y1 = Integer(readfile('y1')) 136 | shape = readfile('shape').strip() 137 | rigid = readfile('rigid').strip() 138 | 139 | safefield = True 140 | safeeq = True 141 | safebase = True 142 | saferho = True 143 | safetransfer = True 144 | safedisc = True 145 | saferigid = True 146 | safeladder = True 147 | safetwist = True 148 | safecomplete = True 149 | safeind = True 150 | 151 | pstatus = 'Unverified' 152 | if not p.is_prime(): pstatus = 'False' 153 | needtofactor.add(p) 154 | if p in V: pstatus = 'True' 155 | if pstatus != 'True': safefield = False 156 | writefile('verify-pisprime',pstatus + '\n') 157 | 158 | pstatus = 'Unverified' 159 | if not l.is_prime(): pstatus = 'False' 160 | needtofactor.add(l) 161 | if l in V: pstatus = 'True' 162 | if pstatus != 'True': safebase = False 163 | writefile('verify-lisprime',pstatus + '\n') 164 | 165 | writefile('expand2-p','= %s\n' % expand2(p)) 166 | writefile('expand2-l','
= %s\n' % expand2(l)) 167 | 168 | writefile('hex-p',hex(p) + '\n') 169 | writefile('hex-l',hex(l) + '\n') 170 | writefile('hex-x0',hex(x0) + '\n') 171 | writefile('hex-x1',hex(x1) + '\n') 172 | writefile('hex-y0',hex(y0) + '\n') 173 | writefile('hex-y1',hex(y1) + '\n') 174 | 175 | gcdlpis1 = gcd(l,p) == 1 176 | safetransfer &= requirement('verify-gcdlp1',gcdlpis1) 177 | 178 | writefile('verify-movsafe','Unverified\n') 179 | writefile('verify-embeddingdegree','Unverified\n') 180 | if gcdlpis1 and l.is_prime(): 181 | u = Integers(l)(p) 182 | d = l-1 183 | needtofactor.add(d) 184 | for v in V: 185 | while d % v == 0: d /= v 186 | if d == 1: 187 | d = l-1 188 | for v in V: 189 | while d % v == 0: 190 | if u^(d/v) != 1: break 191 | d /= v 192 | safetransfer &= requirement('verify-movsafe',(l-1)/d <= 100) 193 | writefile('verify-embeddingdegree','%s
= (l-1)/%s\n' % (d,(l-1)/d)) 194 | 195 | t = p+1-l*round((p+1)/l) 196 | if l^2 > 16*p: 197 | writefile('verify-trace','%s\n' % t) 198 | f = factor(1) 199 | d = (p+1-t)/l 200 | needtofactor.add(d) 201 | for v in V: 202 | while d % v == 0: 203 | d //= v 204 | f *= factor(v) 205 | writefile('verify-cofactor','%s\n' % f) 206 | else: 207 | writefile('verify-trace','Unverified\n') 208 | writefile('verify-cofactor','Unverified\n') 209 | 210 | D = t^2-4*p 211 | needtofactor.add(D) 212 | for v in V: 213 | while D % v^2 == 0: D /= v^2 214 | if prod([v for v in V if D % v == 0]) != -D: 215 | writefile('verify-disc','Unverified\n') 216 | writefile('verify-discisbig','Unverified\n') 217 | safedisc = False 218 | else: 219 | f = -prod([factor(v) for v in V if D % v == 0]) 220 | if D % 4 != 1: 221 | D *= 4 222 | f = factor(4) * f 223 | Dbits = (log(-D)/log(2)).numerical_approx() 224 | writefile('verify-disc','%s
= %s
≈ -2^%.1f\n' % (D,f,Dbits)) 225 | safedisc &= requirement('verify-discisbig',D < -2^100) 226 | 227 | pi4 = 0.78539816339744830961566084581987572105 228 | rho = log(pi4*l)/log(4) 229 | writefile('verify-rho','%.1f\n' % rho) 230 | saferho &= requirement('verify-rhoabove100',rho.numerical_approx() >= 100) 231 | 232 | twistl = 'Unverified' 233 | d = p+1+t 234 | needtofactor.add(d) 235 | for v in V: 236 | while d % v == 0: d /= v 237 | if d == 1: 238 | d = p+1+t 239 | for v in V: 240 | if d % v == 0: 241 | if twistl == 'Unverified' or v > twistl: twistl = v 242 | 243 | writefile('verify-twistl','%s\n' % twistl) 244 | writefile('verify-twistembeddingdegree','Unverified\n') 245 | writefile('verify-twistmovsafe','Unverified\n') 246 | if twistl == 'Unverified': 247 | writefile('hex-twistl','Unverified\n') 248 | writefile('expand2-twistl','Unverified\n') 249 | writefile('verify-twistcofactor','Unverified\n') 250 | writefile('verify-gcdtwistlp1','Unverified\n') 251 | writefile('verify-twistrho','Unverified\n') 252 | safetwist = False 253 | else: 254 | writefile('hex-twistl',hex(twistl) + '\n') 255 | writefile('expand2-twistl','
= %s\n' % expand2(twistl)) 256 | f = factor(1) 257 | d = (p+1+t)/twistl 258 | needtofactor.add(d) 259 | for v in V: 260 | while d % v == 0: 261 | d //= v 262 | f *= factor(v) 263 | writefile('verify-twistcofactor','%s\n' % f) 264 | gcdtwistlpis1 = gcd(twistl,p) == 1 265 | safetwist &= requirement('verify-gcdtwistlp1',gcdtwistlpis1) 266 | 267 | movsafe = 'Unverified' 268 | embeddingdegree = 'Unverified' 269 | if gcdtwistlpis1 and twistl.is_prime(): 270 | u = Integers(twistl)(p) 271 | d = twistl-1 272 | needtofactor.add(d) 273 | for v in V: 274 | while d % v == 0: d /= v 275 | if d == 1: 276 | d = twistl-1 277 | for v in V: 278 | while d % v == 0: 279 | if u^(d/v) != 1: break 280 | d /= v 281 | safetwist &= requirement('verify-twistmovsafe',(twistl-1)/d <= 100) 282 | writefile('verify-twistembeddingdegree',"%s
= (l'-1)/%s\n" % (d,(twistl-1)/d)) 283 | 284 | rho = log(pi4*twistl)/log(4) 285 | writefile('verify-twistrho','%.1f\n' % rho) 286 | safetwist &= requirement('verify-twistrhoabove100',rho.numerical_approx() >= 100) 287 | 288 | precomp = 0 289 | joint = l 290 | needtofactor.add(p+1-t) 291 | needtofactor.add(p+1+t) 292 | for v in V: 293 | d1 = p+1-t 294 | d2 = p+1+t 295 | while d1 % v == 0 or d2 % v == 0: 296 | if d1 % v == 0: d1 //= v 297 | if d2 % v == 0: d2 //= v 298 | # best case for attack: cyclic; each power is usable 299 | # also assume that kangaroo is as efficient as rho 300 | if v + sqrt(pi4*joint/v) < sqrt(pi4*joint): 301 | precomp += v 302 | joint /= v 303 | 304 | rho = log(precomp + sqrt(pi4 * joint))/log(2) 305 | writefile('verify-jointrho','%.1f\n' % rho) 306 | safetwist &= requirement('verify-jointrhoabove100',rho.numerical_approx() >= 100) 307 | 308 | 309 | x0 = k(x0) 310 | y0 = k(y0) 311 | x1 = k(x1) 312 | y1 = k(y1) 313 | 314 | if shape in ('edwards', 'tedwards'): 315 | d = Integer(readfile('d')) 316 | a = 1 317 | if shape == 'tedwards': 318 | a = Integer(readfile('a')) 319 | 320 | writefile('verify-shape','Twisted Edwards\n') 321 | writefile('verify-equation','%sx^2+y^2 = 1%+dx^2y^2\n' % (a, d)) 322 | if a == 1: 323 | writefile('verify-shape','Edwards\n') 324 | writefile('verify-equation','x^2+y^2 = 1%+dx^2y^2\n' % d) 325 | 326 | a = k(a) 327 | d = k(d) 328 | elliptic = a*d*(a-d) 329 | level0 = a*x0^2+y0^2-1-d*x0^2*y0^2 330 | level1 = a*x1^2+y1^2-1-d*x1^2*y1^2 331 | 332 | if shape == 'montgomery': 333 | writefile('verify-shape','Montgomery\n') 334 | A = Integer(readfile('A')) 335 | B = Integer(readfile('B')) 336 | equation = '%sy^2 = x^3%+dx^2+x' % (B,A) 337 | if B == 1: 338 | equation = 'y^2 = x^3%+dx^2+x' % A 339 | writefile('verify-equation',equation + '\n') 340 | 341 | A = k(A) 342 | B = k(B) 343 | elliptic = B*(A^2-4) 344 | level0 = B*y0^2-x0^3-A*x0^2-x0 345 | level1 = B*y1^2-x1^3-A*x1^2-x1 346 | 347 | if shape == 'shortw': 348 | writefile('verify-shape','short Weierstrass\n') 349 | a = Integer(readfile('a')) 350 | b = Integer(readfile('b')) 351 | writefile('verify-equation','y^2 = x^3%+dx%+d\n' % (a,b)) 352 | 353 | a = k(a) 354 | b = k(b) 355 | elliptic = 4*a^3+27*b^2 356 | level0 = y0^2-x0^3-a*x0-b 357 | level1 = y1^2-x1^3-a*x1-b 358 | 359 | writefile('verify-elliptic',str(elliptic) + '\n') 360 | safeeq &= requirement('verify-iselliptic',elliptic != 0) 361 | safebase &= requirement('verify-isoncurve0',level0 == 0) 362 | safebase &= requirement('verify-isoncurve1',level1 == 0) 363 | 364 | if shape in ('edwards', 'tedwards'): 365 | A = 2*(a+d)/(a-d) 366 | B = 4/(a-d) 367 | x0,y0 = (1+y0)/(1-y0),((1+y0)/(1-y0))/x0 368 | x1,y1 = (1+y1)/(1-y1),((1+y1)/(1-y1))/x1 369 | shape = 'montgomery' 370 | 371 | if shape == 'montgomery': 372 | a = (3-A^2)/(3*B^2) 373 | b = (2*A^3-9*A)/(27*B^3) 374 | x0,y0 = (x0+A/3)/B,y0/B 375 | x1,y1 = (x1+A/3)/B,y1/B 376 | shape = 'shortw' 377 | 378 | try: 379 | E = EllipticCurve([a,b]) 380 | numorder2 = 0 381 | numorder4 = 0 382 | for P in E(0).division_points(4): 383 | if P != 0 and 2*P == 0: 384 | numorder2 += 1 385 | if 2*P != 0 and 4*P == 0: 386 | numorder4 += 1 387 | writefile('verify-numorder2',str(numorder2) + '\n') 388 | writefile('verify-numorder4',str(numorder4) + '\n') 389 | completesingle = False 390 | completemulti = False 391 | if numorder4 == 2 and numorder2 == 1: 392 | # complete edwards form, and montgomery with unique point of order 2 393 | completesingle = True 394 | completemulti = True 395 | # should extend this to allow complete twisted hessian 396 | safecomplete &= requirement('verify-completesingle',completesingle) 397 | safecomplete &= requirement('verify-completemulti',completemulti) 398 | safecomplete &= requirement('verify-ltimesbase1is0',l * E([x1,y1]) == 0) 399 | writefile('verify-ltimesbase1',str(l * E([x1,y1])) + '\n') 400 | writefile('verify-cofactorbase01',str(((p+1-t)//l) * E([x0,y0]) == E([x1,y1])) + '\n') 401 | except: 402 | writefile('verify-numorder2','Unverified\n') 403 | writefile('verify-numorder4','Unverified\n') 404 | writefile('verify-ltimesbase1','Unverified\n') 405 | writefile('verify-cofactorbase01','Unverified\n') 406 | safecomplete = False 407 | 408 | montladder = False 409 | for r,e in (z^3+a*z+b).roots(): 410 | if (3*r^2+a).is_square(): 411 | montladder = True 412 | safeladder &= requirement('verify-montladder',montladder) 413 | 414 | indistinguishability = False 415 | elligator2 = False 416 | if (p+1-t) % 2 == 0: 417 | if b != 0: 418 | indistinguishability = True 419 | elligator2 = True 420 | safeind &= requirement('verify-indistinguishability',indistinguishability) 421 | writefile('verify-ind-notes','Elligator 2: %s.\n' % ['No','Yes'][elligator2]) 422 | 423 | saferigid &= (rigid == 'fully rigid' or rigid == 'somewhat rigid') 424 | 425 | safecurve = True 426 | safecurve &= requirement('verify-safefield',safefield) 427 | safecurve &= requirement('verify-safeeq',safeeq) 428 | safecurve &= requirement('verify-safebase',safebase) 429 | safecurve &= requirement('verify-saferho',saferho) 430 | safecurve &= requirement('verify-safetransfer',safetransfer) 431 | safecurve &= requirement('verify-safedisc',safedisc) 432 | safecurve &= requirement('verify-saferigid',saferigid) 433 | safecurve &= requirement('verify-safeladder',safeladder) 434 | safecurve &= requirement('verify-safetwist',safetwist) 435 | safecurve &= requirement('verify-safecomplete',safecomplete) 436 | safecurve &= requirement('verify-safeind',safeind) 437 | requirement('verify-safecurve',safecurve) 438 | 439 | originaldir = os.open('.',os.O_RDONLY) 440 | for i in range(1,len(sys.argv)): 441 | os.fchdir(originaldir) 442 | os.chdir(sys.argv[i]) 443 | verify() 444 | 445 | -------------------------------------------------------------------------------- /src/fr.rs: -------------------------------------------------------------------------------- 1 | //! This module provides an implementation of the Jubjub scalar field $\mathbb{F}_r$ 2 | //! where `r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` 3 | 4 | use core::convert::TryInto; 5 | use core::fmt; 6 | use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 7 | 8 | use ff::{Field, PrimeField}; 9 | use rand_core::RngCore; 10 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 11 | 12 | #[cfg(feature = "bits")] 13 | use ff::{FieldBits, PrimeFieldBits}; 14 | 15 | use crate::util::{adc, mac, sbb}; 16 | 17 | /// Represents an element of the scalar field $\mathbb{F}_r$ of the Jubjub elliptic 18 | /// curve construction. 19 | // The internal representation of this type is four 64-bit unsigned 20 | // integers in little-endian order. Elements of Fr are always in 21 | // Montgomery form; i.e., Fr(a) = aR mod r, with R = 2^256. 22 | #[derive(Clone, Copy, Eq)] 23 | pub struct Fr(pub(crate) [u64; 4]); 24 | 25 | impl fmt::Debug for Fr { 26 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 27 | let tmp = self.to_bytes(); 28 | write!(f, "0x")?; 29 | for &b in tmp.iter().rev() { 30 | write!(f, "{:02x}", b)?; 31 | } 32 | Ok(()) 33 | } 34 | } 35 | 36 | impl fmt::Display for Fr { 37 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 38 | write!(f, "{:?}", self) 39 | } 40 | } 41 | 42 | impl From for Fr { 43 | fn from(val: u64) -> Fr { 44 | Fr([val, 0, 0, 0]) * R2 45 | } 46 | } 47 | 48 | impl ConstantTimeEq for Fr { 49 | fn ct_eq(&self, other: &Self) -> Choice { 50 | self.0[0].ct_eq(&other.0[0]) 51 | & self.0[1].ct_eq(&other.0[1]) 52 | & self.0[2].ct_eq(&other.0[2]) 53 | & self.0[3].ct_eq(&other.0[3]) 54 | } 55 | } 56 | 57 | impl PartialEq for Fr { 58 | #[inline] 59 | fn eq(&self, other: &Self) -> bool { 60 | bool::from(self.ct_eq(other)) 61 | } 62 | } 63 | 64 | impl ConditionallySelectable for Fr { 65 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 66 | Fr([ 67 | u64::conditional_select(&a.0[0], &b.0[0], choice), 68 | u64::conditional_select(&a.0[1], &b.0[1], choice), 69 | u64::conditional_select(&a.0[2], &b.0[2], choice), 70 | u64::conditional_select(&a.0[3], &b.0[3], choice), 71 | ]) 72 | } 73 | } 74 | 75 | /// Constant representing the modulus 76 | /// r = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7 77 | pub const MODULUS: Fr = Fr([ 78 | 0xd097_0e5e_d6f7_2cb7, 79 | 0xa668_2093_ccc8_1082, 80 | 0x0667_3b01_0134_3b00, 81 | 0x0e7d_b4ea_6533_afa9, 82 | ]); 83 | 84 | /// The modulus as u32 limbs. 85 | #[cfg(not(target_pointer_width = "64"))] 86 | const MODULUS_LIMBS_32: [u32; 8] = [ 87 | 0xd6f7_2cb7, 88 | 0xd097_0e5e, 89 | 0xccc8_1082, 90 | 0xa668_2093, 91 | 0x0134_3b00, 92 | 0x0667_3b01, 93 | 0x6533_afa9, 94 | 0x0e7d_b4ea, 95 | ]; 96 | 97 | // The number of bits needed to represent the modulus. 98 | const MODULUS_BITS: u32 = 252; 99 | 100 | /// 2^-1 101 | const TWO_INV: Fr = Fr([ 102 | 0x7b47_8d09_4846_9a48, 103 | 0xccbe_fb61_99bf_7be9, 104 | 0xccc6_27f7_f65e_27fa, 105 | 0x0c12_58ac_d662_82b7, 106 | ]); 107 | 108 | // GENERATOR = 6 (multiplicative generator of r-1 order, that is also quadratic nonresidue) 109 | const GENERATOR: Fr = Fr([ 110 | 0x720b_1b19_d49e_a8f1, 111 | 0xbf4a_a361_01f1_3a58, 112 | 0x5fa8_cc96_8193_ccbb, 113 | 0x0e70_cbdc_7dcc_f3ac, 114 | ]); 115 | 116 | // 2^S * t = MODULUS - 1 with t odd 117 | const S: u32 = 1; 118 | 119 | // 2^S root of unity computed by GENERATOR^t 120 | const ROOT_OF_UNITY: Fr = Fr([ 121 | 0xaa9f_02ab_1d61_24de, 122 | 0xb352_4a64_6611_2932, 123 | 0x7342_2612_15ac_260b, 124 | 0x04d6_b87b_1da2_59e2, 125 | ]); 126 | 127 | /// ROOT_OF_UNITY^-1 (which is equal to ROOT_OF_UNITY because S = 1). 128 | const ROOT_OF_UNITY_INV: Fr = ROOT_OF_UNITY; 129 | 130 | /// GENERATOR^{2^s} where t * 2^s + 1 = q with t odd. 131 | /// In other words, this is a t root of unity. 132 | const DELTA: Fr = Fr([ 133 | 0x994f_5ac0_c8e4_1613, 134 | 0x3bb7_3163_0bbf_0b84, 135 | 0x1df0_a482_0371_a563, 136 | 0x0e30_3e96_f8cb_47bd, 137 | ]); 138 | 139 | impl<'a> Neg for &'a Fr { 140 | type Output = Fr; 141 | 142 | #[inline] 143 | fn neg(self) -> Fr { 144 | self.neg() 145 | } 146 | } 147 | 148 | impl Neg for Fr { 149 | type Output = Fr; 150 | 151 | #[inline] 152 | fn neg(self) -> Fr { 153 | -&self 154 | } 155 | } 156 | 157 | impl<'a, 'b> Sub<&'b Fr> for &'a Fr { 158 | type Output = Fr; 159 | 160 | #[inline] 161 | fn sub(self, rhs: &'b Fr) -> Fr { 162 | self.sub(rhs) 163 | } 164 | } 165 | 166 | impl<'a, 'b> Add<&'b Fr> for &'a Fr { 167 | type Output = Fr; 168 | 169 | #[inline] 170 | fn add(self, rhs: &'b Fr) -> Fr { 171 | self.add(rhs) 172 | } 173 | } 174 | 175 | impl<'a, 'b> Mul<&'b Fr> for &'a Fr { 176 | type Output = Fr; 177 | 178 | #[inline] 179 | fn mul(self, rhs: &'b Fr) -> Fr { 180 | // Schoolbook multiplication 181 | 182 | self.mul(rhs) 183 | } 184 | } 185 | 186 | impl_binops_additive!(Fr, Fr); 187 | impl_binops_multiplicative!(Fr, Fr); 188 | 189 | impl core::iter::Sum for Fr 190 | where 191 | T: core::borrow::Borrow, 192 | { 193 | fn sum(iter: I) -> Self 194 | where 195 | I: Iterator, 196 | { 197 | iter.fold(Self::zero(), |acc, item| acc + item.borrow()) 198 | } 199 | } 200 | 201 | impl core::iter::Product for Fr 202 | where 203 | T: core::borrow::Borrow, 204 | { 205 | fn product(iter: I) -> Self 206 | where 207 | I: Iterator, 208 | { 209 | iter.fold(Self::one(), |acc, item| acc * item.borrow()) 210 | } 211 | } 212 | 213 | /// INV = -(r^{-1} mod 2^64) mod 2^64 214 | const INV: u64 = 0x1ba3_a358_ef78_8ef9; 215 | 216 | /// R = 2^256 mod r 217 | const R: Fr = Fr([ 218 | 0x25f8_0bb3_b996_07d9, 219 | 0xf315_d62f_66b6_e750, 220 | 0x9325_14ee_eb88_14f4, 221 | 0x09a6_fc6f_4791_55c6, 222 | ]); 223 | 224 | /// R^2 = 2^512 mod r 225 | const R2: Fr = Fr([ 226 | 0x6771_9aa4_95e5_7731, 227 | 0x51b0_cef0_9ce3_fc26, 228 | 0x69da_b7fa_c026_e9a5, 229 | 0x04f6_547b_8d12_7688, 230 | ]); 231 | 232 | /// R^3 = 2^768 mod r 233 | const R3: Fr = Fr([ 234 | 0xe0d6_c656_3d83_0544, 235 | 0x323e_3883_598d_0f85, 236 | 0xf0fe_a300_4c2e_2ba8, 237 | 0x0587_4f84_9467_37ec, 238 | ]); 239 | 240 | impl Default for Fr { 241 | fn default() -> Self { 242 | Self::zero() 243 | } 244 | } 245 | 246 | impl Fr { 247 | /// Returns zero, the additive identity. 248 | #[inline] 249 | pub const fn zero() -> Fr { 250 | Fr([0, 0, 0, 0]) 251 | } 252 | 253 | /// Returns one, the multiplicative identity. 254 | #[inline] 255 | pub const fn one() -> Fr { 256 | R 257 | } 258 | 259 | /// Doubles this field element. 260 | #[inline] 261 | pub const fn double(&self) -> Fr { 262 | self.add(self) 263 | } 264 | 265 | /// Attempts to convert a little-endian byte representation of 266 | /// a field element into an element of `Fr`, failing if the input 267 | /// is not canonical (is not smaller than r). 268 | pub fn from_bytes(bytes: &[u8; 32]) -> CtOption { 269 | let mut tmp = Fr([0, 0, 0, 0]); 270 | 271 | tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); 272 | tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); 273 | tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); 274 | tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); 275 | 276 | // Try to subtract the modulus 277 | let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); 278 | let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); 279 | let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); 280 | let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); 281 | 282 | // If the element is smaller than MODULUS then the 283 | // subtraction will underflow, producing a borrow value 284 | // of 0xffff...ffff. Otherwise, it'll be zero. 285 | let is_some = (borrow as u8) & 1; 286 | 287 | // Convert to Montgomery form by computing 288 | // (a.R^0 * R^2) / R = a.R 289 | tmp *= &R2; 290 | 291 | CtOption::new(tmp, Choice::from(is_some)) 292 | } 293 | 294 | /// Converts an element of `Fr` into a byte representation in 295 | /// little-endian byte order. 296 | pub fn to_bytes(&self) -> [u8; 32] { 297 | // Turn into canonical form by computing 298 | // (a.R) / R = a 299 | let tmp = Fr::montgomery_reduce(self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0); 300 | 301 | let mut res = [0; 32]; 302 | res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); 303 | res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); 304 | res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); 305 | res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); 306 | 307 | res 308 | } 309 | 310 | /// Converts a 512-bit little endian integer into 311 | /// an element of Fr by reducing modulo r. 312 | pub fn from_bytes_wide(bytes: &[u8; 64]) -> Fr { 313 | Fr::from_u512([ 314 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 315 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 316 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 317 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 318 | u64::from_le_bytes(bytes[32..40].try_into().unwrap()), 319 | u64::from_le_bytes(bytes[40..48].try_into().unwrap()), 320 | u64::from_le_bytes(bytes[48..56].try_into().unwrap()), 321 | u64::from_le_bytes(bytes[56..64].try_into().unwrap()), 322 | ]) 323 | } 324 | 325 | fn from_u512(limbs: [u64; 8]) -> Fr { 326 | // We reduce an arbitrary 512-bit number by decomposing it into two 256-bit digits 327 | // with the higher bits multiplied by 2^256. Thus, we perform two reductions 328 | // 329 | // 1. the lower bits are multiplied by R^2, as normal 330 | // 2. the upper bits are multiplied by R^2 * 2^256 = R^3 331 | // 332 | // and computing their sum in the field. It remains to see that arbitrary 256-bit 333 | // numbers can be placed into Montgomery form safely using the reduction. The 334 | // reduction works so long as the product is less than R=2^256 multiplied by 335 | // the modulus. This holds because for any `c` smaller than the modulus, we have 336 | // that (2^256 - 1)*c is an acceptable product for the reduction. Therefore, the 337 | // reduction always works so long as `c` is in the field; in this case it is either the 338 | // constant `R2` or `R3`. 339 | let d0 = Fr([limbs[0], limbs[1], limbs[2], limbs[3]]); 340 | let d1 = Fr([limbs[4], limbs[5], limbs[6], limbs[7]]); 341 | // Convert to Montgomery form 342 | d0 * R2 + d1 * R3 343 | } 344 | 345 | /// Converts from an integer represented in little endian 346 | /// into its (congruent) `Fr` representation. 347 | pub const fn from_raw(val: [u64; 4]) -> Self { 348 | (&Fr(val)).mul(&R2) 349 | } 350 | 351 | /// Squares this element. 352 | #[inline] 353 | pub const fn square(&self) -> Fr { 354 | let (r1, carry) = mac(0, self.0[0], self.0[1], 0); 355 | let (r2, carry) = mac(0, self.0[0], self.0[2], carry); 356 | let (r3, r4) = mac(0, self.0[0], self.0[3], carry); 357 | 358 | let (r3, carry) = mac(r3, self.0[1], self.0[2], 0); 359 | let (r4, r5) = mac(r4, self.0[1], self.0[3], carry); 360 | 361 | let (r5, r6) = mac(r5, self.0[2], self.0[3], 0); 362 | 363 | let r7 = r6 >> 63; 364 | let r6 = (r6 << 1) | (r5 >> 63); 365 | let r5 = (r5 << 1) | (r4 >> 63); 366 | let r4 = (r4 << 1) | (r3 >> 63); 367 | let r3 = (r3 << 1) | (r2 >> 63); 368 | let r2 = (r2 << 1) | (r1 >> 63); 369 | let r1 = r1 << 1; 370 | 371 | let (r0, carry) = mac(0, self.0[0], self.0[0], 0); 372 | let (r1, carry) = adc(0, r1, carry); 373 | let (r2, carry) = mac(r2, self.0[1], self.0[1], carry); 374 | let (r3, carry) = adc(0, r3, carry); 375 | let (r4, carry) = mac(r4, self.0[2], self.0[2], carry); 376 | let (r5, carry) = adc(0, r5, carry); 377 | let (r6, carry) = mac(r6, self.0[3], self.0[3], carry); 378 | let (r7, _) = adc(0, r7, carry); 379 | 380 | Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) 381 | } 382 | 383 | /// Computes the square root of this element, if it exists. 384 | pub fn sqrt(&self) -> CtOption { 385 | // Because r = 3 (mod 4) 386 | // sqrt can be done with only one exponentiation, 387 | // via the computation of self^((r + 1) // 4) (mod r) 388 | let sqrt = self.pow_vartime(&[ 389 | 0xb425_c397_b5bd_cb2e, 390 | 0x299a_0824_f332_0420, 391 | 0x4199_cec0_404d_0ec0, 392 | 0x039f_6d3a_994c_ebea, 393 | ]); 394 | 395 | CtOption::new( 396 | sqrt, 397 | (sqrt * sqrt).ct_eq(self), // Only return Some if it's the square root. 398 | ) 399 | } 400 | 401 | /// Exponentiates `self` by `by`, where `by` is a 402 | /// little-endian order integer exponent. 403 | pub fn pow(&self, by: &[u64; 4]) -> Self { 404 | let mut res = Self::one(); 405 | for e in by.iter().rev() { 406 | for i in (0..64).rev() { 407 | res = res.square(); 408 | let mut tmp = res; 409 | tmp.mul_assign(self); 410 | res.conditional_assign(&tmp, (((*e >> i) & 0x1) as u8).into()); 411 | } 412 | } 413 | res 414 | } 415 | 416 | /// Exponentiates `self` by `by`, where `by` is a 417 | /// little-endian order integer exponent. 418 | /// 419 | /// **This operation is variable time with respect 420 | /// to the exponent.** If the exponent is fixed, 421 | /// this operation is effectively constant time. 422 | pub fn pow_vartime(&self, by: &[u64; 4]) -> Self { 423 | let mut res = Self::one(); 424 | for e in by.iter().rev() { 425 | for i in (0..64).rev() { 426 | res = res.square(); 427 | 428 | if ((*e >> i) & 1) == 1 { 429 | res.mul_assign(self); 430 | } 431 | } 432 | } 433 | res 434 | } 435 | 436 | /// Computes the multiplicative inverse of this element, 437 | /// failing if the element is zero. 438 | pub fn invert(&self) -> CtOption { 439 | #[inline(always)] 440 | fn square_assign_multi(n: &mut Fr, num_times: usize) { 441 | for _ in 0..num_times { 442 | *n = n.square(); 443 | } 444 | } 445 | // found using https://github.com/kwantam/addchain 446 | let mut t1 = self.square(); 447 | let mut t0 = t1.square(); 448 | let mut t3 = t0 * t1; 449 | let t6 = t3 * self; 450 | let t7 = t6 * t1; 451 | let t12 = t7 * t3; 452 | let t13 = t12 * t0; 453 | let t16 = t12 * t3; 454 | let t2 = t13 * t3; 455 | let t15 = t16 * t3; 456 | let t19 = t2 * t0; 457 | let t9 = t15 * t3; 458 | let t18 = t9 * t3; 459 | let t14 = t18 * t1; 460 | let t4 = t18 * t0; 461 | let t8 = t18 * t3; 462 | let t17 = t14 * t3; 463 | let t11 = t8 * t3; 464 | t1 = t17 * t3; 465 | let t5 = t11 * t3; 466 | t3 = t5 * t0; 467 | t0 = t5.square(); 468 | square_assign_multi(&mut t0, 5); 469 | t0.mul_assign(&t3); 470 | square_assign_multi(&mut t0, 6); 471 | t0.mul_assign(&t8); 472 | square_assign_multi(&mut t0, 7); 473 | t0.mul_assign(&t19); 474 | square_assign_multi(&mut t0, 6); 475 | t0.mul_assign(&t13); 476 | square_assign_multi(&mut t0, 8); 477 | t0.mul_assign(&t14); 478 | square_assign_multi(&mut t0, 6); 479 | t0.mul_assign(&t18); 480 | square_assign_multi(&mut t0, 7); 481 | t0.mul_assign(&t17); 482 | square_assign_multi(&mut t0, 5); 483 | t0.mul_assign(&t16); 484 | square_assign_multi(&mut t0, 3); 485 | t0.mul_assign(self); 486 | square_assign_multi(&mut t0, 11); 487 | t0.mul_assign(&t11); 488 | square_assign_multi(&mut t0, 8); 489 | t0.mul_assign(&t5); 490 | square_assign_multi(&mut t0, 5); 491 | t0.mul_assign(&t15); 492 | square_assign_multi(&mut t0, 8); 493 | t0.mul_assign(self); 494 | square_assign_multi(&mut t0, 12); 495 | t0.mul_assign(&t13); 496 | square_assign_multi(&mut t0, 7); 497 | t0.mul_assign(&t9); 498 | square_assign_multi(&mut t0, 5); 499 | t0.mul_assign(&t15); 500 | square_assign_multi(&mut t0, 14); 501 | t0.mul_assign(&t14); 502 | square_assign_multi(&mut t0, 5); 503 | t0.mul_assign(&t13); 504 | square_assign_multi(&mut t0, 2); 505 | t0.mul_assign(self); 506 | square_assign_multi(&mut t0, 6); 507 | t0.mul_assign(self); 508 | square_assign_multi(&mut t0, 9); 509 | t0.mul_assign(&t7); 510 | square_assign_multi(&mut t0, 6); 511 | t0.mul_assign(&t12); 512 | square_assign_multi(&mut t0, 8); 513 | t0.mul_assign(&t11); 514 | square_assign_multi(&mut t0, 3); 515 | t0.mul_assign(self); 516 | square_assign_multi(&mut t0, 12); 517 | t0.mul_assign(&t9); 518 | square_assign_multi(&mut t0, 11); 519 | t0.mul_assign(&t8); 520 | square_assign_multi(&mut t0, 8); 521 | t0.mul_assign(&t7); 522 | square_assign_multi(&mut t0, 4); 523 | t0.mul_assign(&t6); 524 | square_assign_multi(&mut t0, 10); 525 | t0.mul_assign(&t5); 526 | square_assign_multi(&mut t0, 7); 527 | t0.mul_assign(&t3); 528 | square_assign_multi(&mut t0, 6); 529 | t0.mul_assign(&t4); 530 | square_assign_multi(&mut t0, 7); 531 | t0.mul_assign(&t3); 532 | square_assign_multi(&mut t0, 5); 533 | t0.mul_assign(&t2); 534 | square_assign_multi(&mut t0, 6); 535 | t0.mul_assign(&t2); 536 | square_assign_multi(&mut t0, 7); 537 | t0.mul_assign(&t1); 538 | 539 | CtOption::new(t0, !self.ct_eq(&Self::zero())) 540 | } 541 | 542 | #[inline] 543 | #[allow(clippy::too_many_arguments)] 544 | const fn montgomery_reduce( 545 | r0: u64, 546 | r1: u64, 547 | r2: u64, 548 | r3: u64, 549 | r4: u64, 550 | r5: u64, 551 | r6: u64, 552 | r7: u64, 553 | ) -> Self { 554 | // The Montgomery reduction here is based on Algorithm 14.32 in 555 | // Handbook of Applied Cryptography 556 | // . 557 | 558 | let k = r0.wrapping_mul(INV); 559 | let (_, carry) = mac(r0, k, MODULUS.0[0], 0); 560 | let (r1, carry) = mac(r1, k, MODULUS.0[1], carry); 561 | let (r2, carry) = mac(r2, k, MODULUS.0[2], carry); 562 | let (r3, carry) = mac(r3, k, MODULUS.0[3], carry); 563 | let (r4, carry2) = adc(r4, 0, carry); 564 | 565 | let k = r1.wrapping_mul(INV); 566 | let (_, carry) = mac(r1, k, MODULUS.0[0], 0); 567 | let (r2, carry) = mac(r2, k, MODULUS.0[1], carry); 568 | let (r3, carry) = mac(r3, k, MODULUS.0[2], carry); 569 | let (r4, carry) = mac(r4, k, MODULUS.0[3], carry); 570 | let (r5, carry2) = adc(r5, carry2, carry); 571 | 572 | let k = r2.wrapping_mul(INV); 573 | let (_, carry) = mac(r2, k, MODULUS.0[0], 0); 574 | let (r3, carry) = mac(r3, k, MODULUS.0[1], carry); 575 | let (r4, carry) = mac(r4, k, MODULUS.0[2], carry); 576 | let (r5, carry) = mac(r5, k, MODULUS.0[3], carry); 577 | let (r6, carry2) = adc(r6, carry2, carry); 578 | 579 | let k = r3.wrapping_mul(INV); 580 | let (_, carry) = mac(r3, k, MODULUS.0[0], 0); 581 | let (r4, carry) = mac(r4, k, MODULUS.0[1], carry); 582 | let (r5, carry) = mac(r5, k, MODULUS.0[2], carry); 583 | let (r6, carry) = mac(r6, k, MODULUS.0[3], carry); 584 | let (r7, _) = adc(r7, carry2, carry); 585 | 586 | // Result may be within MODULUS of the correct value 587 | (&Fr([r4, r5, r6, r7])).sub(&MODULUS) 588 | } 589 | 590 | /// Multiplies this element by another element 591 | #[inline] 592 | pub const fn mul(&self, rhs: &Self) -> Self { 593 | // Schoolbook multiplication 594 | 595 | let (r0, carry) = mac(0, self.0[0], rhs.0[0], 0); 596 | let (r1, carry) = mac(0, self.0[0], rhs.0[1], carry); 597 | let (r2, carry) = mac(0, self.0[0], rhs.0[2], carry); 598 | let (r3, r4) = mac(0, self.0[0], rhs.0[3], carry); 599 | 600 | let (r1, carry) = mac(r1, self.0[1], rhs.0[0], 0); 601 | let (r2, carry) = mac(r2, self.0[1], rhs.0[1], carry); 602 | let (r3, carry) = mac(r3, self.0[1], rhs.0[2], carry); 603 | let (r4, r5) = mac(r4, self.0[1], rhs.0[3], carry); 604 | 605 | let (r2, carry) = mac(r2, self.0[2], rhs.0[0], 0); 606 | let (r3, carry) = mac(r3, self.0[2], rhs.0[1], carry); 607 | let (r4, carry) = mac(r4, self.0[2], rhs.0[2], carry); 608 | let (r5, r6) = mac(r5, self.0[2], rhs.0[3], carry); 609 | 610 | let (r3, carry) = mac(r3, self.0[3], rhs.0[0], 0); 611 | let (r4, carry) = mac(r4, self.0[3], rhs.0[1], carry); 612 | let (r5, carry) = mac(r5, self.0[3], rhs.0[2], carry); 613 | let (r6, r7) = mac(r6, self.0[3], rhs.0[3], carry); 614 | 615 | Fr::montgomery_reduce(r0, r1, r2, r3, r4, r5, r6, r7) 616 | } 617 | 618 | /// Subtracts another element from this element. 619 | #[inline] 620 | pub const fn sub(&self, rhs: &Self) -> Self { 621 | let (d0, borrow) = sbb(self.0[0], rhs.0[0], 0); 622 | let (d1, borrow) = sbb(self.0[1], rhs.0[1], borrow); 623 | let (d2, borrow) = sbb(self.0[2], rhs.0[2], borrow); 624 | let (d3, borrow) = sbb(self.0[3], rhs.0[3], borrow); 625 | 626 | // If underflow occurred on the final limb, borrow = 0xfff...fff, otherwise 627 | // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the modulus. 628 | let (d0, carry) = adc(d0, MODULUS.0[0] & borrow, 0); 629 | let (d1, carry) = adc(d1, MODULUS.0[1] & borrow, carry); 630 | let (d2, carry) = adc(d2, MODULUS.0[2] & borrow, carry); 631 | let (d3, _) = adc(d3, MODULUS.0[3] & borrow, carry); 632 | 633 | Fr([d0, d1, d2, d3]) 634 | } 635 | 636 | /// Adds this element to another element. 637 | #[inline] 638 | pub const fn add(&self, rhs: &Self) -> Self { 639 | let (d0, carry) = adc(self.0[0], rhs.0[0], 0); 640 | let (d1, carry) = adc(self.0[1], rhs.0[1], carry); 641 | let (d2, carry) = adc(self.0[2], rhs.0[2], carry); 642 | let (d3, _) = adc(self.0[3], rhs.0[3], carry); 643 | 644 | // Attempt to subtract the modulus, to ensure the value 645 | // is smaller than the modulus. 646 | (&Fr([d0, d1, d2, d3])).sub(&MODULUS) 647 | } 648 | 649 | /// Negates this element. 650 | #[inline] 651 | pub const fn neg(&self) -> Self { 652 | // Subtract `self` from `MODULUS` to negate. Ignore the final 653 | // borrow because it cannot underflow; self is guaranteed to 654 | // be in the field. 655 | let (d0, borrow) = sbb(MODULUS.0[0], self.0[0], 0); 656 | let (d1, borrow) = sbb(MODULUS.0[1], self.0[1], borrow); 657 | let (d2, borrow) = sbb(MODULUS.0[2], self.0[2], borrow); 658 | let (d3, _) = sbb(MODULUS.0[3], self.0[3], borrow); 659 | 660 | // `tmp` could be `MODULUS` if `self` was zero. Create a mask that is 661 | // zero if `self` was zero, and `u64::max_value()` if self was nonzero. 662 | let mask = (((self.0[0] | self.0[1] | self.0[2] | self.0[3]) == 0) as u64).wrapping_sub(1); 663 | 664 | Fr([d0 & mask, d1 & mask, d2 & mask, d3 & mask]) 665 | } 666 | } 667 | 668 | impl From for [u8; 32] { 669 | fn from(value: Fr) -> [u8; 32] { 670 | value.to_bytes() 671 | } 672 | } 673 | 674 | impl<'a> From<&'a Fr> for [u8; 32] { 675 | fn from(value: &'a Fr) -> [u8; 32] { 676 | value.to_bytes() 677 | } 678 | } 679 | 680 | impl Field for Fr { 681 | const ZERO: Self = Self::zero(); 682 | const ONE: Self = Self::one(); 683 | 684 | fn random(mut rng: impl RngCore) -> Self { 685 | let mut buf = [0; 64]; 686 | rng.fill_bytes(&mut buf); 687 | Self::from_bytes_wide(&buf) 688 | } 689 | 690 | #[must_use] 691 | fn square(&self) -> Self { 692 | self.square() 693 | } 694 | 695 | #[must_use] 696 | fn double(&self) -> Self { 697 | self.double() 698 | } 699 | 700 | fn invert(&self) -> CtOption { 701 | self.invert() 702 | } 703 | 704 | fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) { 705 | ff::helpers::sqrt_ratio_generic(num, div) 706 | } 707 | 708 | fn sqrt(&self) -> CtOption { 709 | self.sqrt() 710 | } 711 | } 712 | 713 | impl PrimeField for Fr { 714 | type Repr = [u8; 32]; 715 | 716 | fn from_repr(r: Self::Repr) -> CtOption { 717 | Self::from_bytes(&r) 718 | } 719 | 720 | fn to_repr(&self) -> Self::Repr { 721 | self.to_bytes() 722 | } 723 | 724 | fn is_odd(&self) -> Choice { 725 | Choice::from(self.to_bytes()[0] & 1) 726 | } 727 | 728 | const MODULUS: &'static str = 729 | "0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7"; 730 | const NUM_BITS: u32 = MODULUS_BITS; 731 | const CAPACITY: u32 = Self::NUM_BITS - 1; 732 | const TWO_INV: Self = TWO_INV; 733 | const MULTIPLICATIVE_GENERATOR: Self = GENERATOR; 734 | const S: u32 = S; 735 | const ROOT_OF_UNITY: Self = ROOT_OF_UNITY; 736 | const ROOT_OF_UNITY_INV: Self = ROOT_OF_UNITY_INV; 737 | const DELTA: Self = DELTA; 738 | } 739 | 740 | #[cfg(all(feature = "bits", not(target_pointer_width = "64")))] 741 | type ReprBits = [u32; 8]; 742 | 743 | #[cfg(all(feature = "bits", target_pointer_width = "64"))] 744 | type ReprBits = [u64; 4]; 745 | 746 | #[cfg(feature = "bits")] 747 | impl PrimeFieldBits for Fr { 748 | type ReprBits = ReprBits; 749 | 750 | fn to_le_bits(&self) -> FieldBits { 751 | let bytes = self.to_bytes(); 752 | 753 | #[cfg(not(target_pointer_width = "64"))] 754 | let limbs = [ 755 | u32::from_le_bytes(bytes[0..4].try_into().unwrap()), 756 | u32::from_le_bytes(bytes[4..8].try_into().unwrap()), 757 | u32::from_le_bytes(bytes[8..12].try_into().unwrap()), 758 | u32::from_le_bytes(bytes[12..16].try_into().unwrap()), 759 | u32::from_le_bytes(bytes[16..20].try_into().unwrap()), 760 | u32::from_le_bytes(bytes[20..24].try_into().unwrap()), 761 | u32::from_le_bytes(bytes[24..28].try_into().unwrap()), 762 | u32::from_le_bytes(bytes[28..32].try_into().unwrap()), 763 | ]; 764 | 765 | #[cfg(target_pointer_width = "64")] 766 | let limbs = [ 767 | u64::from_le_bytes(bytes[0..8].try_into().unwrap()), 768 | u64::from_le_bytes(bytes[8..16].try_into().unwrap()), 769 | u64::from_le_bytes(bytes[16..24].try_into().unwrap()), 770 | u64::from_le_bytes(bytes[24..32].try_into().unwrap()), 771 | ]; 772 | 773 | FieldBits::new(limbs) 774 | } 775 | 776 | fn char_le_bits() -> FieldBits { 777 | #[cfg(not(target_pointer_width = "64"))] 778 | { 779 | FieldBits::new(MODULUS_LIMBS_32) 780 | } 781 | 782 | #[cfg(target_pointer_width = "64")] 783 | FieldBits::new(MODULUS.0) 784 | } 785 | } 786 | 787 | #[test] 788 | fn test_constants() { 789 | assert_eq!( 790 | Fr::MODULUS, 791 | "0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7", 792 | ); 793 | 794 | assert_eq!(Fr::from(2) * Fr::TWO_INV, Fr::ONE); 795 | 796 | assert_eq!(Fr::ROOT_OF_UNITY * Fr::ROOT_OF_UNITY_INV, Fr::ONE); 797 | 798 | // ROOT_OF_UNITY^{2^s} mod m == 1 799 | assert_eq!(Fr::ROOT_OF_UNITY.pow(&[1u64 << Fr::S, 0, 0, 0]), Fr::ONE); 800 | 801 | // DELTA^{t} mod m == 1 802 | assert_eq!( 803 | Fr::DELTA.pow(&[ 804 | 0x684b_872f_6b7b_965b, 805 | 0x5334_1049_e664_0841, 806 | 0x8333_9d80_809a_1d80, 807 | 0x073e_da75_3299_d7d4, 808 | ]), 809 | Fr::ONE, 810 | ); 811 | } 812 | 813 | #[test] 814 | fn test_inv() { 815 | // Compute -(r^{-1} mod 2^64) mod 2^64 by exponentiating 816 | // by totient(2**64) - 1 817 | 818 | let mut inv = 1u64; 819 | for _ in 0..63 { 820 | inv = inv.wrapping_mul(inv); 821 | inv = inv.wrapping_mul(MODULUS.0[0]); 822 | } 823 | inv = inv.wrapping_neg(); 824 | 825 | assert_eq!(inv, INV); 826 | } 827 | 828 | #[test] 829 | fn test_debug() { 830 | assert_eq!( 831 | format!("{:?}", Fr::zero()), 832 | "0x0000000000000000000000000000000000000000000000000000000000000000" 833 | ); 834 | assert_eq!( 835 | format!("{:?}", Fr::one()), 836 | "0x0000000000000000000000000000000000000000000000000000000000000001" 837 | ); 838 | assert_eq!( 839 | format!("{:?}", R2), 840 | "0x09a6fc6f479155c6932514eeeb8814f4f315d62f66b6e75025f80bb3b99607d9" 841 | ); 842 | } 843 | 844 | #[allow(clippy::eq_op)] 845 | #[test] 846 | fn test_equality() { 847 | assert_eq!(Fr::zero(), Fr::zero()); 848 | assert_eq!(Fr::one(), Fr::one()); 849 | assert_eq!(R2, R2); 850 | 851 | assert!(Fr::zero() != Fr::one()); 852 | assert!(Fr::one() != R2); 853 | } 854 | 855 | #[test] 856 | fn test_to_bytes() { 857 | assert_eq!( 858 | Fr::zero().to_bytes(), 859 | [ 860 | 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, 861 | 0, 0, 0 862 | ] 863 | ); 864 | 865 | assert_eq!( 866 | Fr::one().to_bytes(), 867 | [ 868 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 869 | 0, 0, 0 870 | ] 871 | ); 872 | 873 | assert_eq!( 874 | R2.to_bytes(), 875 | [ 876 | 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, 877 | 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 878 | ] 879 | ); 880 | 881 | assert_eq!( 882 | (-&Fr::one()).to_bytes(), 883 | [ 884 | 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 885 | 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 886 | ] 887 | ); 888 | } 889 | 890 | #[test] 891 | fn test_from_bytes() { 892 | assert_eq!( 893 | Fr::from_bytes(&[ 894 | 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, 895 | 0, 0, 0 896 | ]) 897 | .unwrap(), 898 | Fr::zero() 899 | ); 900 | 901 | assert_eq!( 902 | Fr::from_bytes(&[ 903 | 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 904 | 0, 0, 0 905 | ]) 906 | .unwrap(), 907 | Fr::one() 908 | ); 909 | 910 | assert_eq!( 911 | Fr::from_bytes(&[ 912 | 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, 913 | 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9 914 | ]) 915 | .unwrap(), 916 | R2 917 | ); 918 | 919 | // -1 should work 920 | assert!(bool::from( 921 | Fr::from_bytes(&[ 922 | 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 923 | 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 924 | ]) 925 | .is_some() 926 | )); 927 | 928 | // modulus is invalid 929 | assert!(bool::from( 930 | Fr::from_bytes(&[ 931 | 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 932 | 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 933 | ]) 934 | .is_none() 935 | )); 936 | 937 | // Anything larger than the modulus is invalid 938 | assert!(bool::from( 939 | Fr::from_bytes(&[ 940 | 184, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 941 | 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14 942 | ]) 943 | .is_none() 944 | )); 945 | 946 | assert!(bool::from( 947 | Fr::from_bytes(&[ 948 | 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 949 | 1, 1, 59, 104, 6, 169, 175, 51, 101, 234, 180, 125, 14 950 | ]) 951 | .is_none() 952 | )); 953 | 954 | assert!(bool::from( 955 | Fr::from_bytes(&[ 956 | 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 957 | 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 15 958 | ]) 959 | .is_none() 960 | )); 961 | } 962 | 963 | #[test] 964 | fn test_from_u512_zero() { 965 | assert_eq!( 966 | Fr::zero(), 967 | Fr::from_u512([ 968 | MODULUS.0[0], 969 | MODULUS.0[1], 970 | MODULUS.0[2], 971 | MODULUS.0[3], 972 | 0, 973 | 0, 974 | 0, 975 | 0 976 | ]) 977 | ); 978 | } 979 | 980 | #[test] 981 | fn test_from_u512_r() { 982 | assert_eq!(R, Fr::from_u512([1, 0, 0, 0, 0, 0, 0, 0])); 983 | } 984 | 985 | #[test] 986 | fn test_from_u512_r2() { 987 | assert_eq!(R2, Fr::from_u512([0, 0, 0, 0, 1, 0, 0, 0])); 988 | } 989 | 990 | #[test] 991 | fn test_from_u512_max() { 992 | let max_u64 = 0xffff_ffff_ffff_ffff; 993 | assert_eq!( 994 | R3 - R, 995 | Fr::from_u512([max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64, max_u64]) 996 | ); 997 | } 998 | 999 | #[test] 1000 | fn test_from_bytes_wide_r2() { 1001 | assert_eq!( 1002 | R2, 1003 | Fr::from_bytes_wide(&[ 1004 | 217, 7, 150, 185, 179, 11, 248, 37, 80, 231, 182, 102, 47, 214, 21, 243, 244, 20, 136, 1005 | 235, 238, 20, 37, 147, 198, 85, 145, 71, 111, 252, 166, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1006 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1007 | ]) 1008 | ); 1009 | } 1010 | 1011 | #[test] 1012 | fn test_from_bytes_wide_negative_one() { 1013 | assert_eq!( 1014 | -&Fr::one(), 1015 | Fr::from_bytes_wide(&[ 1016 | 182, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1017 | 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1018 | 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1019 | ]) 1020 | ); 1021 | } 1022 | 1023 | #[test] 1024 | fn test_from_bytes_wide_maximum() { 1025 | assert_eq!( 1026 | Fr([ 1027 | 0x8b75_c901_5ae4_2a22, 1028 | 0xe590_82e7_bf9e_38b8, 1029 | 0x6440_c912_61da_51b3, 1030 | 0x0a5e_07ff_b209_91cf, 1031 | ]), 1032 | Fr::from_bytes_wide(&[0xff; 64]) 1033 | ); 1034 | } 1035 | 1036 | #[test] 1037 | fn test_zero() { 1038 | assert_eq!(Fr::zero(), -&Fr::zero()); 1039 | assert_eq!(Fr::zero(), Fr::zero() + Fr::zero()); 1040 | assert_eq!(Fr::zero(), Fr::zero() - Fr::zero()); 1041 | assert_eq!(Fr::zero(), Fr::zero() * Fr::zero()); 1042 | } 1043 | 1044 | #[cfg(test)] 1045 | const LARGEST: Fr = Fr([ 1046 | 0xd097_0e5e_d6f7_2cb6, 1047 | 0xa668_2093_ccc8_1082, 1048 | 0x0667_3b01_0134_3b00, 1049 | 0x0e7d_b4ea_6533_afa9, 1050 | ]); 1051 | 1052 | #[test] 1053 | fn test_addition() { 1054 | let mut tmp = LARGEST; 1055 | tmp += &LARGEST; 1056 | 1057 | assert_eq!( 1058 | tmp, 1059 | Fr([ 1060 | 0xd097_0e5e_d6f7_2cb5, 1061 | 0xa668_2093_ccc8_1082, 1062 | 0x0667_3b01_0134_3b00, 1063 | 0x0e7d_b4ea_6533_afa9 1064 | ]) 1065 | ); 1066 | 1067 | let mut tmp = LARGEST; 1068 | tmp += &Fr([1, 0, 0, 0]); 1069 | 1070 | assert_eq!(tmp, Fr::zero()); 1071 | } 1072 | 1073 | #[test] 1074 | fn test_negation() { 1075 | let tmp = -&LARGEST; 1076 | 1077 | assert_eq!(tmp, Fr([1, 0, 0, 0])); 1078 | 1079 | let tmp = -&Fr::zero(); 1080 | assert_eq!(tmp, Fr::zero()); 1081 | let tmp = -&Fr([1, 0, 0, 0]); 1082 | assert_eq!(tmp, LARGEST); 1083 | } 1084 | 1085 | #[test] 1086 | fn test_subtraction() { 1087 | let mut tmp = LARGEST; 1088 | tmp -= &LARGEST; 1089 | 1090 | assert_eq!(tmp, Fr::zero()); 1091 | 1092 | let mut tmp = Fr::zero(); 1093 | tmp -= &LARGEST; 1094 | 1095 | let mut tmp2 = MODULUS; 1096 | tmp2 -= &LARGEST; 1097 | 1098 | assert_eq!(tmp, tmp2); 1099 | } 1100 | 1101 | #[test] 1102 | fn test_multiplication() { 1103 | let mut cur = LARGEST; 1104 | 1105 | for _ in 0..100 { 1106 | let mut tmp = cur; 1107 | tmp *= &cur; 1108 | 1109 | let mut tmp2 = Fr::zero(); 1110 | for b in cur 1111 | .to_bytes() 1112 | .iter() 1113 | .rev() 1114 | .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) 1115 | { 1116 | let tmp3 = tmp2; 1117 | tmp2.add_assign(&tmp3); 1118 | 1119 | if b { 1120 | tmp2.add_assign(&cur); 1121 | } 1122 | } 1123 | 1124 | assert_eq!(tmp, tmp2); 1125 | 1126 | cur.add_assign(&LARGEST); 1127 | } 1128 | } 1129 | 1130 | #[test] 1131 | fn test_squaring() { 1132 | let mut cur = LARGEST; 1133 | 1134 | for _ in 0..100 { 1135 | let mut tmp = cur; 1136 | tmp = tmp.square(); 1137 | 1138 | let mut tmp2 = Fr::zero(); 1139 | for b in cur 1140 | .to_bytes() 1141 | .iter() 1142 | .rev() 1143 | .flat_map(|byte| (0..8).rev().map(move |i| ((byte >> i) & 1u8) == 1u8)) 1144 | { 1145 | let tmp3 = tmp2; 1146 | tmp2.add_assign(&tmp3); 1147 | 1148 | if b { 1149 | tmp2.add_assign(&cur); 1150 | } 1151 | } 1152 | 1153 | assert_eq!(tmp, tmp2); 1154 | 1155 | cur.add_assign(&LARGEST); 1156 | } 1157 | } 1158 | 1159 | #[test] 1160 | fn test_inversion() { 1161 | assert!(bool::from(Fr::zero().invert().is_none())); 1162 | assert_eq!(Fr::one().invert().unwrap(), Fr::one()); 1163 | assert_eq!((-&Fr::one()).invert().unwrap(), -&Fr::one()); 1164 | 1165 | let mut tmp = R2; 1166 | 1167 | for _ in 0..100 { 1168 | let mut tmp2 = tmp.invert().unwrap(); 1169 | tmp2.mul_assign(&tmp); 1170 | 1171 | assert_eq!(tmp2, Fr::one()); 1172 | 1173 | tmp.add_assign(&R2); 1174 | } 1175 | } 1176 | 1177 | #[test] 1178 | fn test_invert_is_pow() { 1179 | let r_minus_2 = [ 1180 | 0xd097_0e5e_d6f7_2cb5, 1181 | 0xa668_2093_ccc8_1082, 1182 | 0x0667_3b01_0134_3b00, 1183 | 0x0e7d_b4ea_6533_afa9, 1184 | ]; 1185 | 1186 | let mut r1 = R; 1187 | let mut r2 = R; 1188 | let mut r3 = R; 1189 | 1190 | for _ in 0..100 { 1191 | r1 = r1.invert().unwrap(); 1192 | r2 = r2.pow_vartime(&r_minus_2); 1193 | r3 = r3.pow(&r_minus_2); 1194 | 1195 | assert_eq!(r1, r2); 1196 | assert_eq!(r2, r3); 1197 | // Add R so we check something different next time around 1198 | r1.add_assign(&R); 1199 | r2 = r1; 1200 | r3 = r1; 1201 | } 1202 | } 1203 | 1204 | #[test] 1205 | fn test_sqrt() { 1206 | let mut square = Fr([ 1207 | // r - 2 1208 | 0xd097_0e5e_d6f7_2cb5, 1209 | 0xa668_2093_ccc8_1082, 1210 | 0x0667_3b01_0134_3b00, 1211 | 0x0e7d_b4ea_6533_afa9, 1212 | ]); 1213 | 1214 | let mut none_count = 0; 1215 | 1216 | for _ in 0..100 { 1217 | let square_root = square.sqrt(); 1218 | if bool::from(square_root.is_none()) { 1219 | none_count += 1; 1220 | } else { 1221 | assert_eq!(square_root.unwrap() * square_root.unwrap(), square); 1222 | } 1223 | square -= Fr::one(); 1224 | } 1225 | 1226 | assert_eq!(47, none_count); 1227 | } 1228 | 1229 | #[test] 1230 | fn test_from_raw() { 1231 | assert_eq!( 1232 | Fr::from_raw([ 1233 | 0x25f8_0bb3_b996_07d8, 1234 | 0xf315_d62f_66b6_e750, 1235 | 0x9325_14ee_eb88_14f4, 1236 | 0x09a6_fc6f_4791_55c6, 1237 | ]), 1238 | Fr::from_raw([0xffff_ffff_ffff_ffff; 4]) 1239 | ); 1240 | 1241 | assert_eq!(Fr::from_raw(MODULUS.0), Fr::zero()); 1242 | 1243 | assert_eq!(Fr::from_raw([1, 0, 0, 0]), R); 1244 | } 1245 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! This crate provides an implementation of the **Jubjub** elliptic curve and its associated 2 | //! field arithmetic. See [`README.md`](https://github.com/zkcrypto/jubjub/blob/master/README.md) for more details about Jubjub. 3 | //! 4 | //! # API 5 | //! 6 | //! * `AffinePoint` / `ExtendedPoint` which are implementations of Jubjub group arithmetic 7 | //! * `AffineNielsPoint` / `ExtendedNielsPoint` which are pre-processed Jubjub points 8 | //! * `Fq`, which is the base field of Jubjub 9 | //! * `Fr`, which is the scalar field of Jubjub 10 | //! * `batch_normalize` for converting many `ExtendedPoint`s into `AffinePoint`s efficiently. 11 | //! 12 | //! # Constant Time 13 | //! 14 | //! All operations are constant time unless explicitly noted; these functions will contain 15 | //! "vartime" in their name and they will be documented as variable time. 16 | //! 17 | //! This crate uses the `subtle` crate to perform constant-time operations. 18 | 19 | #![no_std] 20 | // Catch documentation errors caused by code changes. 21 | #![deny(rustdoc::broken_intra_doc_links)] 22 | #![deny(missing_debug_implementations)] 23 | #![deny(missing_docs)] 24 | #![deny(unsafe_code)] 25 | // This lint is described at 26 | // https://rust-lang.github.io/rust-clippy/master/index.html#suspicious_arithmetic_impl 27 | // In our library, some of the arithmetic will necessarily involve various binary 28 | // operators, and so this lint is triggered unnecessarily. 29 | #![allow(clippy::suspicious_arithmetic_impl)] 30 | 31 | #[cfg(feature = "alloc")] 32 | extern crate alloc; 33 | 34 | #[cfg(test)] 35 | #[macro_use] 36 | extern crate std; 37 | 38 | use bitvec::{order::Lsb0, view::AsBits}; 39 | use core::borrow::Borrow; 40 | use core::fmt; 41 | use core::iter::Sum; 42 | use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; 43 | use ff::{BatchInverter, Field}; 44 | use group::{ 45 | cofactor::{CofactorCurve, CofactorCurveAffine, CofactorGroup}, 46 | prime::PrimeGroup, 47 | Curve, Group, GroupEncoding, 48 | }; 49 | use rand_core::RngCore; 50 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; 51 | 52 | #[cfg(feature = "alloc")] 53 | use alloc::vec::Vec; 54 | 55 | #[cfg(feature = "alloc")] 56 | use group::WnafGroup; 57 | 58 | #[macro_use] 59 | mod util; 60 | 61 | mod fr; 62 | pub use bls12_381::Scalar as Fq; 63 | pub use fr::Fr; 64 | 65 | /// Represents an element of the base field $\mathbb{F}_q$ of the Jubjub elliptic curve 66 | /// construction. 67 | pub type Base = Fq; 68 | 69 | /// Represents an element of the scalar field $\mathbb{F}_r$ of the Jubjub elliptic curve 70 | /// construction. 71 | pub type Scalar = Fr; 72 | 73 | const FR_MODULUS_BYTES: [u8; 32] = [ 74 | 183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166, 0, 59, 52, 1, 1, 59, 75 | 103, 6, 169, 175, 51, 101, 234, 180, 125, 14, 76 | ]; 77 | 78 | /// This represents a Jubjub point in the affine `(u, v)` 79 | /// coordinates. 80 | #[derive(Clone, Copy, Debug, Eq)] 81 | pub struct AffinePoint { 82 | u: Fq, 83 | v: Fq, 84 | } 85 | 86 | impl fmt::Display for AffinePoint { 87 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 88 | write!(f, "{:?}", self) 89 | } 90 | } 91 | 92 | impl Neg for AffinePoint { 93 | type Output = AffinePoint; 94 | 95 | /// This computes the negation of a point `P = (u, v)` 96 | /// as `-P = (-u, v)`. 97 | #[inline] 98 | fn neg(self) -> AffinePoint { 99 | AffinePoint { 100 | u: -self.u, 101 | v: self.v, 102 | } 103 | } 104 | } 105 | 106 | impl ConstantTimeEq for AffinePoint { 107 | fn ct_eq(&self, other: &Self) -> Choice { 108 | self.u.ct_eq(&other.u) & self.v.ct_eq(&other.v) 109 | } 110 | } 111 | 112 | impl PartialEq for AffinePoint { 113 | fn eq(&self, other: &Self) -> bool { 114 | bool::from(self.ct_eq(other)) 115 | } 116 | } 117 | 118 | impl ConditionallySelectable for AffinePoint { 119 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 120 | AffinePoint { 121 | u: Fq::conditional_select(&a.u, &b.u, choice), 122 | v: Fq::conditional_select(&a.v, &b.v, choice), 123 | } 124 | } 125 | } 126 | 127 | /// This represents an extended point `(U, V, Z, T1, T2)` 128 | /// with `Z` nonzero, corresponding to the affine point 129 | /// `(U/Z, V/Z)`. We always have `T1 * T2 = UV/Z`. 130 | /// 131 | /// You can do the following things with a point in this 132 | /// form: 133 | /// 134 | /// * Convert it into a point in the affine form. 135 | /// * Add it to an `ExtendedPoint`, `AffineNielsPoint` or `ExtendedNielsPoint`. 136 | /// * Double it using `double()`. 137 | /// * Compare it with another extended point using `PartialEq` or `ct_eq()`. 138 | #[derive(Clone, Copy, Debug, Eq)] 139 | pub struct ExtendedPoint { 140 | u: Fq, 141 | v: Fq, 142 | z: Fq, 143 | t1: Fq, 144 | t2: Fq, 145 | } 146 | 147 | impl fmt::Display for ExtendedPoint { 148 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 149 | write!(f, "{:?}", self) 150 | } 151 | } 152 | 153 | impl ConstantTimeEq for ExtendedPoint { 154 | fn ct_eq(&self, other: &Self) -> Choice { 155 | // (u/z, v/z) = (u'/z', v'/z') is implied by 156 | // (uz'z = u'z'z) and 157 | // (vz'z = v'z'z) 158 | // as z and z' are always nonzero. 159 | 160 | (self.u * other.z).ct_eq(&(other.u * self.z)) 161 | & (self.v * other.z).ct_eq(&(other.v * self.z)) 162 | } 163 | } 164 | 165 | impl ConditionallySelectable for ExtendedPoint { 166 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 167 | ExtendedPoint { 168 | u: Fq::conditional_select(&a.u, &b.u, choice), 169 | v: Fq::conditional_select(&a.v, &b.v, choice), 170 | z: Fq::conditional_select(&a.z, &b.z, choice), 171 | t1: Fq::conditional_select(&a.t1, &b.t1, choice), 172 | t2: Fq::conditional_select(&a.t2, &b.t2, choice), 173 | } 174 | } 175 | } 176 | 177 | impl PartialEq for ExtendedPoint { 178 | fn eq(&self, other: &Self) -> bool { 179 | bool::from(self.ct_eq(other)) 180 | } 181 | } 182 | 183 | impl Sum for ExtendedPoint 184 | where 185 | T: Borrow, 186 | { 187 | fn sum(iter: I) -> Self 188 | where 189 | I: Iterator, 190 | { 191 | iter.fold(Self::identity(), |acc, item| acc + item.borrow()) 192 | } 193 | } 194 | 195 | impl Neg for ExtendedPoint { 196 | type Output = ExtendedPoint; 197 | 198 | /// Computes the negation of a point `P = (U, V, Z, T)` 199 | /// as `-P = (-U, V, Z, -T1, T2)`. The choice of `T1` 200 | /// is made without loss of generality. 201 | #[inline] 202 | fn neg(self) -> ExtendedPoint { 203 | ExtendedPoint { 204 | u: -self.u, 205 | v: self.v, 206 | z: self.z, 207 | t1: -self.t1, 208 | t2: self.t2, 209 | } 210 | } 211 | } 212 | 213 | impl From for ExtendedPoint { 214 | /// Constructs an extended point (with `Z = 1`) from 215 | /// an affine point using the map `(u, v) => (u, v, 1, u, v)`. 216 | fn from(affine: AffinePoint) -> ExtendedPoint { 217 | ExtendedPoint { 218 | u: affine.u, 219 | v: affine.v, 220 | z: Fq::one(), 221 | t1: affine.u, 222 | t2: affine.v, 223 | } 224 | } 225 | } 226 | 227 | impl<'a> From<&'a ExtendedPoint> for AffinePoint { 228 | /// Constructs an affine point from an extended point 229 | /// using the map `(U, V, Z, T1, T2) => (U/Z, V/Z)` 230 | /// as Z is always nonzero. **This requires a field inversion 231 | /// and so it is recommended to perform these in a batch 232 | /// using [`batch_normalize`](crate::batch_normalize) instead.** 233 | fn from(extended: &'a ExtendedPoint) -> AffinePoint { 234 | // Z coordinate is always nonzero, so this is 235 | // its inverse. 236 | let zinv = extended.z.invert().unwrap(); 237 | 238 | AffinePoint { 239 | u: extended.u * zinv, 240 | v: extended.v * zinv, 241 | } 242 | } 243 | } 244 | 245 | impl From for AffinePoint { 246 | fn from(extended: ExtendedPoint) -> AffinePoint { 247 | AffinePoint::from(&extended) 248 | } 249 | } 250 | 251 | /// This is a pre-processed version of an affine point `(u, v)` 252 | /// in the form `(v + u, v - u, u * v * 2d)`. This can be added to an 253 | /// [`ExtendedPoint`](crate::ExtendedPoint). 254 | #[derive(Clone, Copy, Debug)] 255 | pub struct AffineNielsPoint { 256 | v_plus_u: Fq, 257 | v_minus_u: Fq, 258 | t2d: Fq, 259 | } 260 | 261 | impl AffineNielsPoint { 262 | /// Constructs this point from the neutral element `(0, 1)`. 263 | pub const fn identity() -> Self { 264 | AffineNielsPoint { 265 | v_plus_u: Fq::one(), 266 | v_minus_u: Fq::one(), 267 | t2d: Fq::zero(), 268 | } 269 | } 270 | 271 | #[inline] 272 | fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { 273 | let zero = AffineNielsPoint::identity(); 274 | 275 | let mut acc = ExtendedPoint::identity(); 276 | 277 | // This is a simple double-and-add implementation of point 278 | // multiplication, moving from most significant to least 279 | // significant bit of the scalar. 280 | // 281 | // We skip the leading four bits because they're always 282 | // unset for Fr. 283 | for bit in by 284 | .as_bits::() 285 | .iter() 286 | .rev() 287 | .skip(4) 288 | .map(|bit| Choice::from(if *bit { 1 } else { 0 })) 289 | { 290 | acc = acc.double(); 291 | acc += AffineNielsPoint::conditional_select(&zero, self, bit); 292 | } 293 | 294 | acc 295 | } 296 | 297 | /// Multiplies this point by the specific little-endian bit pattern in the 298 | /// given byte array, ignoring the highest four bits. 299 | pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { 300 | self.multiply(by) 301 | } 302 | } 303 | 304 | impl<'a, 'b> Mul<&'b Fr> for &'a AffineNielsPoint { 305 | type Output = ExtendedPoint; 306 | 307 | fn mul(self, other: &'b Fr) -> ExtendedPoint { 308 | self.multiply(&other.to_bytes()) 309 | } 310 | } 311 | 312 | impl_binops_multiplicative_mixed!(AffineNielsPoint, Fr, ExtendedPoint); 313 | 314 | impl ConditionallySelectable for AffineNielsPoint { 315 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 316 | AffineNielsPoint { 317 | v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), 318 | v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), 319 | t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), 320 | } 321 | } 322 | } 323 | 324 | /// This is a pre-processed version of an extended point `(U, V, Z, T1, T2)` 325 | /// in the form `(V + U, V - U, Z, T1 * T2 * 2d)`. 326 | #[derive(Clone, Copy, Debug)] 327 | pub struct ExtendedNielsPoint { 328 | v_plus_u: Fq, 329 | v_minus_u: Fq, 330 | z: Fq, 331 | t2d: Fq, 332 | } 333 | 334 | impl ConditionallySelectable for ExtendedNielsPoint { 335 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 336 | ExtendedNielsPoint { 337 | v_plus_u: Fq::conditional_select(&a.v_plus_u, &b.v_plus_u, choice), 338 | v_minus_u: Fq::conditional_select(&a.v_minus_u, &b.v_minus_u, choice), 339 | z: Fq::conditional_select(&a.z, &b.z, choice), 340 | t2d: Fq::conditional_select(&a.t2d, &b.t2d, choice), 341 | } 342 | } 343 | } 344 | 345 | impl ExtendedNielsPoint { 346 | /// Constructs this point from the neutral element `(0, 1)`. 347 | pub const fn identity() -> Self { 348 | ExtendedNielsPoint { 349 | v_plus_u: Fq::one(), 350 | v_minus_u: Fq::one(), 351 | z: Fq::one(), 352 | t2d: Fq::zero(), 353 | } 354 | } 355 | 356 | #[inline] 357 | fn multiply(&self, by: &[u8; 32]) -> ExtendedPoint { 358 | let zero = ExtendedNielsPoint::identity(); 359 | 360 | let mut acc = ExtendedPoint::identity(); 361 | 362 | // This is a simple double-and-add implementation of point 363 | // multiplication, moving from most significant to least 364 | // significant bit of the scalar. 365 | // 366 | // We skip the leading four bits because they're always 367 | // unset for Fr. 368 | for bit in by 369 | .iter() 370 | .rev() 371 | .flat_map(|byte| (0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))) 372 | .skip(4) 373 | { 374 | acc = acc.double(); 375 | acc += ExtendedNielsPoint::conditional_select(&zero, self, bit); 376 | } 377 | 378 | acc 379 | } 380 | 381 | /// Multiplies this point by the specific little-endian bit pattern in the 382 | /// given byte array, ignoring the highest four bits. 383 | pub fn multiply_bits(&self, by: &[u8; 32]) -> ExtendedPoint { 384 | self.multiply(by) 385 | } 386 | } 387 | 388 | impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint { 389 | type Output = ExtendedPoint; 390 | 391 | fn mul(self, other: &'b Fr) -> ExtendedPoint { 392 | self.multiply(&other.to_bytes()) 393 | } 394 | } 395 | 396 | impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, ExtendedPoint); 397 | 398 | // `d = -(10240/10241)` 399 | const EDWARDS_D: Fq = Fq::from_raw([ 400 | 0x0106_5fd6_d634_3eb1, 401 | 0x292d_7f6d_3757_9d26, 402 | 0xf5fd_9207_e6bd_7fd4, 403 | 0x2a93_18e7_4bfa_2b48, 404 | ]); 405 | 406 | // `2*d` 407 | const EDWARDS_D2: Fq = Fq::from_raw([ 408 | 0x020c_bfad_ac68_7d62, 409 | 0x525a_feda_6eaf_3a4c, 410 | 0xebfb_240f_cd7a_ffa8, 411 | 0x5526_31ce_97f4_5691, 412 | ]); 413 | 414 | impl AffinePoint { 415 | /// Constructs the neutral element `(0, 1)`. 416 | pub const fn identity() -> Self { 417 | AffinePoint { 418 | u: Fq::zero(), 419 | v: Fq::one(), 420 | } 421 | } 422 | 423 | /// Determines if this point is the identity. 424 | pub fn is_identity(&self) -> Choice { 425 | ExtendedPoint::from(*self).is_identity() 426 | } 427 | 428 | /// Multiplies this point by the cofactor, producing an 429 | /// `ExtendedPoint` 430 | pub fn mul_by_cofactor(&self) -> ExtendedPoint { 431 | ExtendedPoint::from(*self).mul_by_cofactor() 432 | } 433 | 434 | /// Determines if this point is of small order. 435 | pub fn is_small_order(&self) -> Choice { 436 | ExtendedPoint::from(*self).is_small_order() 437 | } 438 | 439 | /// Determines if this point is torsion free and so is 440 | /// in the prime order subgroup. 441 | pub fn is_torsion_free(&self) -> Choice { 442 | ExtendedPoint::from(*self).is_torsion_free() 443 | } 444 | 445 | /// Determines if this point is prime order, or in other words that 446 | /// the smallest scalar multiplied by this point that produces the 447 | /// identity is `r`. This is equivalent to checking that the point 448 | /// is both torsion free and not the identity. 449 | pub fn is_prime_order(&self) -> Choice { 450 | let extended = ExtendedPoint::from(*self); 451 | extended.is_torsion_free() & (!extended.is_identity()) 452 | } 453 | 454 | /// Converts this element into its byte representation. 455 | pub fn to_bytes(&self) -> [u8; 32] { 456 | let mut tmp = self.v.to_bytes(); 457 | let u = self.u.to_bytes(); 458 | 459 | // Encode the sign of the u-coordinate in the most 460 | // significant bit. 461 | tmp[31] |= u[0] << 7; 462 | 463 | tmp 464 | } 465 | 466 | /// Attempts to interpret a byte representation of an 467 | /// affine point, failing if the element is not on 468 | /// the curve or non-canonical. 469 | pub fn from_bytes(b: [u8; 32]) -> CtOption { 470 | Self::from_bytes_inner(b, 1.into()) 471 | } 472 | 473 | /// Attempts to interpret a byte representation of an affine point, failing if the 474 | /// element is not on the curve. 475 | /// 476 | /// Most non-canonical encodings will also cause a failure. However, this API 477 | /// preserves (for use in consensus-critical protocols) a bug in the parsing code that 478 | /// caused two non-canonical encodings to be **silently** accepted: 479 | /// 480 | /// - (0, 1), which is the identity; 481 | /// - (0, -1), which is a point of order two. 482 | /// 483 | /// Each of these has a single non-canonical encoding in which the value of the sign 484 | /// bit is 1. 485 | /// 486 | /// See [ZIP 216](https://zips.z.cash/zip-0216) for a more detailed description of the 487 | /// bug, as well as its fix. 488 | pub fn from_bytes_pre_zip216_compatibility(b: [u8; 32]) -> CtOption { 489 | Self::from_bytes_inner(b, 0.into()) 490 | } 491 | 492 | fn from_bytes_inner(mut b: [u8; 32], zip_216_enabled: Choice) -> CtOption { 493 | // Grab the sign bit from the representation 494 | let sign = b[31] >> 7; 495 | 496 | // Mask away the sign bit 497 | b[31] &= 0b0111_1111; 498 | 499 | // Interpret what remains as the v-coordinate 500 | Fq::from_bytes(&b).and_then(|v| { 501 | // -u^2 + v^2 = 1 + d.u^2.v^2 502 | // -u^2 = 1 + d.u^2.v^2 - v^2 (rearrange) 503 | // -u^2 - d.u^2.v^2 = 1 - v^2 (rearrange) 504 | // u^2 + d.u^2.v^2 = v^2 - 1 (flip signs) 505 | // u^2 (1 + d.v^2) = v^2 - 1 (factor) 506 | // u^2 = (v^2 - 1) / (1 + d.v^2) (isolate u^2) 507 | // We know that (1 + d.v^2) is nonzero for all v: 508 | // (1 + d.v^2) = 0 509 | // d.v^2 = -1 510 | // v^2 = -(1 / d) No solutions, as -(1 / d) is not a square 511 | 512 | let v2 = v.square(); 513 | 514 | ((v2 - Fq::one()) * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) 515 | .sqrt() 516 | .and_then(|u| { 517 | // Fix the sign of `u` if necessary 518 | let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); 519 | let u_negated = -u; 520 | let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); 521 | 522 | // If u == 0, flip_sign == sign_bit. We therefore want to reject the 523 | // encoding as non-canonical if all of the following occur: 524 | // - ZIP 216 is enabled 525 | // - u == 0 526 | // - flip_sign == true 527 | let u_is_zero = u.ct_eq(&Fq::zero()); 528 | CtOption::new( 529 | AffinePoint { u: final_u, v }, 530 | !(zip_216_enabled & u_is_zero & flip_sign), 531 | ) 532 | }) 533 | }) 534 | } 535 | 536 | /// Attempts to interpret a batch of byte representations of affine points. 537 | /// 538 | /// Returns None for each element if it is not on the curve, or is non-canonical 539 | /// according to ZIP 216. 540 | #[cfg(feature = "alloc")] 541 | pub fn batch_from_bytes(items: impl Iterator) -> Vec> { 542 | use ff::BatchInvert; 543 | 544 | #[derive(Clone, Copy, Default)] 545 | struct Item { 546 | sign: u8, 547 | v: Fq, 548 | numerator: Fq, 549 | denominator: Fq, 550 | } 551 | 552 | impl ConditionallySelectable for Item { 553 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 554 | Item { 555 | sign: u8::conditional_select(&a.sign, &b.sign, choice), 556 | v: Fq::conditional_select(&a.v, &b.v, choice), 557 | numerator: Fq::conditional_select(&a.numerator, &b.numerator, choice), 558 | denominator: Fq::conditional_select(&a.denominator, &b.denominator, choice), 559 | } 560 | } 561 | } 562 | 563 | let items: Vec<_> = items 564 | .map(|mut b| { 565 | // Grab the sign bit from the representation 566 | let sign = b[31] >> 7; 567 | 568 | // Mask away the sign bit 569 | b[31] &= 0b0111_1111; 570 | 571 | // Interpret what remains as the v-coordinate 572 | Fq::from_bytes(&b).map(|v| { 573 | // -u^2 + v^2 = 1 + d.u^2.v^2 574 | // -u^2 = 1 + d.u^2.v^2 - v^2 (rearrange) 575 | // -u^2 - d.u^2.v^2 = 1 - v^2 (rearrange) 576 | // u^2 + d.u^2.v^2 = v^2 - 1 (flip signs) 577 | // u^2 (1 + d.v^2) = v^2 - 1 (factor) 578 | // u^2 = (v^2 - 1) / (1 + d.v^2) (isolate u^2) 579 | // We know that (1 + d.v^2) is nonzero for all v: 580 | // (1 + d.v^2) = 0 581 | // d.v^2 = -1 582 | // v^2 = -(1 / d) No solutions, as -(1 / d) is not a square 583 | 584 | let v2 = v.square(); 585 | 586 | Item { 587 | v, 588 | sign, 589 | numerator: (v2 - Fq::one()), 590 | denominator: Fq::one() + EDWARDS_D * v2, 591 | } 592 | }) 593 | }) 594 | .collect(); 595 | 596 | let mut denominators: Vec<_> = items 597 | .iter() 598 | .map(|item| item.map(|item| item.denominator).unwrap_or(Fq::zero())) 599 | .collect(); 600 | denominators.iter_mut().batch_invert(); 601 | 602 | items 603 | .into_iter() 604 | .zip(denominators.into_iter()) 605 | .map(|(item, inv_denominator)| { 606 | item.and_then( 607 | |Item { 608 | v, sign, numerator, .. 609 | }| { 610 | (numerator * inv_denominator).sqrt().and_then(|u| { 611 | // Fix the sign of `u` if necessary 612 | let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); 613 | let u_negated = -u; 614 | let final_u = Fq::conditional_select(&u, &u_negated, flip_sign); 615 | 616 | // If u == 0, flip_sign == sign_bit. We therefore want to reject the 617 | // encoding as non-canonical if all of the following occur: 618 | // - u == 0 619 | // - flip_sign == true 620 | let u_is_zero = u.ct_eq(&Fq::zero()); 621 | CtOption::new(AffinePoint { u: final_u, v }, !(u_is_zero & flip_sign)) 622 | }) 623 | }, 624 | ) 625 | }) 626 | .collect() 627 | } 628 | 629 | /// Returns the `u`-coordinate of this point. 630 | pub fn get_u(&self) -> Fq { 631 | self.u 632 | } 633 | 634 | /// Returns the `v`-coordinate of this point. 635 | pub fn get_v(&self) -> Fq { 636 | self.v 637 | } 638 | 639 | /// Returns an `ExtendedPoint` for use in arithmetic operations. 640 | pub const fn to_extended(&self) -> ExtendedPoint { 641 | ExtendedPoint { 642 | u: self.u, 643 | v: self.v, 644 | z: Fq::one(), 645 | t1: self.u, 646 | t2: self.v, 647 | } 648 | } 649 | 650 | /// Performs a pre-processing step that produces an `AffineNielsPoint` 651 | /// for use in multiple additions. 652 | pub const fn to_niels(&self) -> AffineNielsPoint { 653 | AffineNielsPoint { 654 | v_plus_u: Fq::add(&self.v, &self.u), 655 | v_minus_u: Fq::sub(&self.v, &self.u), 656 | t2d: Fq::mul(&Fq::mul(&self.u, &self.v), &EDWARDS_D2), 657 | } 658 | } 659 | 660 | /// Constructs an AffinePoint given `u` and `v` without checking 661 | /// that the point is on the curve. 662 | pub const fn from_raw_unchecked(u: Fq, v: Fq) -> AffinePoint { 663 | AffinePoint { u, v } 664 | } 665 | 666 | /// This is only for debugging purposes and not 667 | /// exposed in the public API. Checks that this 668 | /// point is on the curve. 669 | #[cfg(test)] 670 | fn is_on_curve_vartime(&self) -> bool { 671 | let u2 = self.u.square(); 672 | let v2 = self.v.square(); 673 | 674 | v2 - u2 == Fq::one() + EDWARDS_D * u2 * v2 675 | } 676 | } 677 | 678 | impl ExtendedPoint { 679 | /// Constructs an extended point from the neutral element `(0, 1)`. 680 | pub const fn identity() -> Self { 681 | ExtendedPoint { 682 | u: Fq::zero(), 683 | v: Fq::one(), 684 | z: Fq::one(), 685 | t1: Fq::zero(), 686 | t2: Fq::zero(), 687 | } 688 | } 689 | 690 | /// Determines if this point is the identity. 691 | pub fn is_identity(&self) -> Choice { 692 | // If this point is the identity, then 693 | // u = 0 * z = 0 694 | // and v = 1 * z = z 695 | self.u.ct_eq(&Fq::zero()) & self.v.ct_eq(&self.z) 696 | } 697 | 698 | /// Determines if this point is of small order. 699 | pub fn is_small_order(&self) -> Choice { 700 | // We only need to perform two doublings, since the 2-torsion 701 | // points are (0, 1) and (0, -1), and so we only need to check 702 | // that the u-coordinate of the result is zero to see if the 703 | // point is small order. 704 | self.double().double().u.ct_eq(&Fq::zero()) 705 | } 706 | 707 | /// Determines if this point is torsion free and so is contained 708 | /// in the prime order subgroup. 709 | pub fn is_torsion_free(&self) -> Choice { 710 | self.multiply(&FR_MODULUS_BYTES).is_identity() 711 | } 712 | 713 | /// Determines if this point is prime order, or in other words that 714 | /// the smallest scalar multiplied by this point that produces the 715 | /// identity is `r`. This is equivalent to checking that the point 716 | /// is both torsion free and not the identity. 717 | pub fn is_prime_order(&self) -> Choice { 718 | self.is_torsion_free() & (!self.is_identity()) 719 | } 720 | 721 | /// Multiplies this element by the cofactor `8`. 722 | pub fn mul_by_cofactor(&self) -> ExtendedPoint { 723 | self.double().double().double() 724 | } 725 | 726 | /// Performs a pre-processing step that produces an `ExtendedNielsPoint` 727 | /// for use in multiple additions. 728 | pub fn to_niels(&self) -> ExtendedNielsPoint { 729 | ExtendedNielsPoint { 730 | v_plus_u: self.v + self.u, 731 | v_minus_u: self.v - self.u, 732 | z: self.z, 733 | t2d: self.t1 * self.t2 * EDWARDS_D2, 734 | } 735 | } 736 | 737 | /// Computes the doubling of a point more efficiently than a point can 738 | /// be added to itself. 739 | pub fn double(&self) -> ExtendedPoint { 740 | // Doubling is more efficient (three multiplications, four squarings) 741 | // when we work within the projective coordinate space (U:Z, V:Z). We 742 | // rely on the most efficient formula, "dbl-2008-bbjlp", as described 743 | // in Section 6 of "Twisted Edwards Curves" by Bernstein et al. 744 | // 745 | // See 746 | // for more information. 747 | // 748 | // We differ from the literature in that we use (u, v) rather than 749 | // (x, y) coordinates. We also have the constant `a = -1` implied. Let 750 | // us rewrite the procedure of doubling (u, v, z) to produce (U, V, Z) 751 | // as follows: 752 | // 753 | // B = (u + v)^2 754 | // C = u^2 755 | // D = v^2 756 | // F = D - C 757 | // H = 2 * z^2 758 | // J = F - H 759 | // U = (B - C - D) * J 760 | // V = F * (- C - D) 761 | // Z = F * J 762 | // 763 | // If we compute K = D + C, we can rewrite this: 764 | // 765 | // B = (u + v)^2 766 | // C = u^2 767 | // D = v^2 768 | // F = D - C 769 | // K = D + C 770 | // H = 2 * z^2 771 | // J = F - H 772 | // U = (B - K) * J 773 | // V = F * (-K) 774 | // Z = F * J 775 | // 776 | // In order to avoid the unnecessary negation of K, 777 | // we will negate J, transforming the result into 778 | // an equivalent point with a negated z-coordinate. 779 | // 780 | // B = (u + v)^2 781 | // C = u^2 782 | // D = v^2 783 | // F = D - C 784 | // K = D + C 785 | // H = 2 * z^2 786 | // J = H - F 787 | // U = (B - K) * J 788 | // V = F * K 789 | // Z = F * J 790 | // 791 | // Let us rename some variables to simplify: 792 | // 793 | // UV2 = (u + v)^2 794 | // UU = u^2 795 | // VV = v^2 796 | // VVmUU = VV - UU 797 | // VVpUU = VV + UU 798 | // ZZ2 = 2 * z^2 799 | // J = ZZ2 - VVmUU 800 | // U = (UV2 - VVpUU) * J 801 | // V = VVmUU * VVpUU 802 | // Z = VVmUU * J 803 | // 804 | // We wish to obtain two factors of T = UV/Z. 805 | // 806 | // UV/Z = (UV2 - VVpUU) * (ZZ2 - VVmUU) * VVmUU * VVpUU / VVmUU / (ZZ2 - VVmUU) 807 | // = (UV2 - VVpUU) * VVmUU * VVpUU / VVmUU 808 | // = (UV2 - VVpUU) * VVpUU 809 | // 810 | // and so we have that T1 = (UV2 - VVpUU) and T2 = VVpUU. 811 | 812 | let uu = self.u.square(); 813 | let vv = self.v.square(); 814 | let zz2 = self.z.square().double(); 815 | let uv2 = (self.u + self.v).square(); 816 | let vv_plus_uu = vv + uu; 817 | let vv_minus_uu = vv - uu; 818 | 819 | // The remaining arithmetic is exactly the process of converting 820 | // from a completed point to an extended point. 821 | CompletedPoint { 822 | u: uv2 - vv_plus_uu, 823 | v: vv_plus_uu, 824 | z: vv_minus_uu, 825 | t: zz2 - vv_minus_uu, 826 | } 827 | .into_extended() 828 | } 829 | 830 | #[inline] 831 | fn multiply(self, by: &[u8; 32]) -> Self { 832 | self.to_niels().multiply(by) 833 | } 834 | 835 | /// Converts a batch of projective elements into affine elements. 836 | /// 837 | /// This function will panic if `p.len() != q.len()`. 838 | /// 839 | /// This costs 5 multiplications per element, and a field inversion. 840 | fn batch_normalize(p: &[Self], q: &mut [AffinePoint]) { 841 | assert_eq!(p.len(), q.len()); 842 | 843 | for (p, q) in p.iter().zip(q.iter_mut()) { 844 | // We use the `u` field of `AffinePoint` to store the z-coordinate being 845 | // inverted, and the `v` field for scratch space. 846 | q.u = p.z; 847 | } 848 | 849 | BatchInverter::invert_with_internal_scratch(q, |q| &mut q.u, |q| &mut q.v); 850 | 851 | for (p, q) in p.iter().zip(q.iter_mut()).rev() { 852 | let tmp = q.u; 853 | 854 | // Set the coordinates to the correct value 855 | q.u = p.u * tmp; // Multiply by 1/z 856 | q.v = p.v * tmp; // Multiply by 1/z 857 | } 858 | } 859 | 860 | /// This is only for debugging purposes and not 861 | /// exposed in the public API. Checks that this 862 | /// point is on the curve. 863 | #[cfg(test)] 864 | fn is_on_curve_vartime(&self) -> bool { 865 | let affine = AffinePoint::from(*self); 866 | 867 | self.z != Fq::zero() 868 | && affine.is_on_curve_vartime() 869 | && (affine.u * affine.v * self.z == self.t1 * self.t2) 870 | } 871 | } 872 | 873 | impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedPoint { 874 | type Output = ExtendedPoint; 875 | 876 | fn mul(self, other: &'b Fr) -> ExtendedPoint { 877 | self.multiply(&other.to_bytes()) 878 | } 879 | } 880 | 881 | impl_binops_multiplicative!(ExtendedPoint, Fr); 882 | 883 | impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a ExtendedPoint { 884 | type Output = ExtendedPoint; 885 | 886 | #[allow(clippy::suspicious_arithmetic_impl)] 887 | fn add(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { 888 | // We perform addition in the extended coordinates. Here we use 889 | // a formula presented by Hisil, Wong, Carter and Dawson in 890 | // "Twisted Edward Curves Revisited" which only requires 8M. 891 | // 892 | // A = (V1 - U1) * (V2 - U2) 893 | // B = (V1 + U1) * (V2 + U2) 894 | // C = 2d * T1 * T2 895 | // D = 2 * Z1 * Z2 896 | // E = B - A 897 | // F = D - C 898 | // G = D + C 899 | // H = B + A 900 | // U3 = E * F 901 | // Y3 = G * H 902 | // Z3 = F * G 903 | // T3 = E * H 904 | 905 | let a = (self.v - self.u) * other.v_minus_u; 906 | let b = (self.v + self.u) * other.v_plus_u; 907 | let c = self.t1 * self.t2 * other.t2d; 908 | let d = (self.z * other.z).double(); 909 | 910 | // The remaining arithmetic is exactly the process of converting 911 | // from a completed point to an extended point. 912 | CompletedPoint { 913 | u: b - a, 914 | v: b + a, 915 | z: d + c, 916 | t: d - c, 917 | } 918 | .into_extended() 919 | } 920 | } 921 | 922 | impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a ExtendedPoint { 923 | type Output = ExtendedPoint; 924 | 925 | #[allow(clippy::suspicious_arithmetic_impl)] 926 | fn sub(self, other: &'b ExtendedNielsPoint) -> ExtendedPoint { 927 | let a = (self.v - self.u) * other.v_plus_u; 928 | let b = (self.v + self.u) * other.v_minus_u; 929 | let c = self.t1 * self.t2 * other.t2d; 930 | let d = (self.z * other.z).double(); 931 | 932 | CompletedPoint { 933 | u: b - a, 934 | v: b + a, 935 | z: d - c, 936 | t: d + c, 937 | } 938 | .into_extended() 939 | } 940 | } 941 | 942 | impl_binops_additive!(ExtendedPoint, ExtendedNielsPoint); 943 | 944 | impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a ExtendedPoint { 945 | type Output = ExtendedPoint; 946 | 947 | #[allow(clippy::suspicious_arithmetic_impl)] 948 | fn add(self, other: &'b AffineNielsPoint) -> ExtendedPoint { 949 | // This is identical to the addition formula for `ExtendedNielsPoint`, 950 | // except we can assume that `other.z` is one, so that we perform 951 | // 7 multiplications. 952 | 953 | let a = (self.v - self.u) * other.v_minus_u; 954 | let b = (self.v + self.u) * other.v_plus_u; 955 | let c = self.t1 * self.t2 * other.t2d; 956 | let d = self.z.double(); 957 | 958 | // The remaining arithmetic is exactly the process of converting 959 | // from a completed point to an extended point. 960 | CompletedPoint { 961 | u: b - a, 962 | v: b + a, 963 | z: d + c, 964 | t: d - c, 965 | } 966 | .into_extended() 967 | } 968 | } 969 | 970 | impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a ExtendedPoint { 971 | type Output = ExtendedPoint; 972 | 973 | #[allow(clippy::suspicious_arithmetic_impl)] 974 | fn sub(self, other: &'b AffineNielsPoint) -> ExtendedPoint { 975 | let a = (self.v - self.u) * other.v_plus_u; 976 | let b = (self.v + self.u) * other.v_minus_u; 977 | let c = self.t1 * self.t2 * other.t2d; 978 | let d = self.z.double(); 979 | 980 | CompletedPoint { 981 | u: b - a, 982 | v: b + a, 983 | z: d - c, 984 | t: d + c, 985 | } 986 | .into_extended() 987 | } 988 | } 989 | 990 | impl_binops_additive!(ExtendedPoint, AffineNielsPoint); 991 | 992 | impl<'a, 'b> Add<&'b ExtendedPoint> for &'a ExtendedPoint { 993 | type Output = ExtendedPoint; 994 | 995 | #[inline] 996 | fn add(self, other: &'b ExtendedPoint) -> ExtendedPoint { 997 | self + other.to_niels() 998 | } 999 | } 1000 | 1001 | impl<'a, 'b> Sub<&'b ExtendedPoint> for &'a ExtendedPoint { 1002 | type Output = ExtendedPoint; 1003 | 1004 | #[inline] 1005 | fn sub(self, other: &'b ExtendedPoint) -> ExtendedPoint { 1006 | self - other.to_niels() 1007 | } 1008 | } 1009 | 1010 | impl_binops_additive!(ExtendedPoint, ExtendedPoint); 1011 | 1012 | impl<'a, 'b> Add<&'b AffinePoint> for &'a ExtendedPoint { 1013 | type Output = ExtendedPoint; 1014 | 1015 | #[inline] 1016 | fn add(self, other: &'b AffinePoint) -> ExtendedPoint { 1017 | self + other.to_niels() 1018 | } 1019 | } 1020 | 1021 | impl<'a, 'b> Sub<&'b AffinePoint> for &'a ExtendedPoint { 1022 | type Output = ExtendedPoint; 1023 | 1024 | #[inline] 1025 | fn sub(self, other: &'b AffinePoint) -> ExtendedPoint { 1026 | self - other.to_niels() 1027 | } 1028 | } 1029 | 1030 | impl_binops_additive!(ExtendedPoint, AffinePoint); 1031 | 1032 | /// This is a "completed" point produced during a point doubling or 1033 | /// addition routine. These points exist in the `(U:Z, V:T)` model 1034 | /// of the curve. This is not exposed in the API because it is 1035 | /// an implementation detail. 1036 | struct CompletedPoint { 1037 | u: Fq, 1038 | v: Fq, 1039 | z: Fq, 1040 | t: Fq, 1041 | } 1042 | 1043 | impl CompletedPoint { 1044 | /// This converts a completed point into an extended point by 1045 | /// homogenizing: 1046 | /// 1047 | /// (u/z, v/t) = (u/z * t/t, v/t * z/z) = (ut/zt, vz/zt) 1048 | /// 1049 | /// The resulting T coordinate is utvz/zt = uv, and so 1050 | /// T1 = u, T2 = v, without loss of generality. 1051 | #[inline] 1052 | fn into_extended(self) -> ExtendedPoint { 1053 | ExtendedPoint { 1054 | u: self.u * self.t, 1055 | v: self.v * self.z, 1056 | z: self.z * self.t, 1057 | t1: self.u, 1058 | t2: self.v, 1059 | } 1060 | } 1061 | } 1062 | 1063 | impl Default for AffinePoint { 1064 | /// Returns the identity. 1065 | fn default() -> AffinePoint { 1066 | AffinePoint::identity() 1067 | } 1068 | } 1069 | 1070 | impl Default for ExtendedPoint { 1071 | /// Returns the identity. 1072 | fn default() -> ExtendedPoint { 1073 | ExtendedPoint::identity() 1074 | } 1075 | } 1076 | 1077 | /// This takes a mutable slice of `ExtendedPoint`s and "normalizes" them using 1078 | /// only a single inversion for the entire batch. This normalization results in 1079 | /// all of the points having a Z-coordinate of one. Further, an iterator is 1080 | /// returned which can be used to obtain `AffinePoint`s for each element in the 1081 | /// slice. 1082 | /// 1083 | /// This costs 5 multiplications per element, and a field inversion. 1084 | pub fn batch_normalize(v: &mut [ExtendedPoint]) -> impl Iterator + '_ { 1085 | // We use the `t1` field of `ExtendedPoint` for scratch space. 1086 | BatchInverter::invert_with_internal_scratch(v, |p| &mut p.z, |p| &mut p.t1); 1087 | 1088 | for p in v.iter_mut() { 1089 | let mut q = *p; 1090 | let tmp = q.z; 1091 | 1092 | // Set the coordinates to the correct value 1093 | q.u *= &tmp; // Multiply by 1/z 1094 | q.v *= &tmp; // Multiply by 1/z 1095 | q.z = Fq::one(); // z-coordinate is now one 1096 | q.t1 = q.u; 1097 | q.t2 = q.v; 1098 | 1099 | *p = q; 1100 | } 1101 | 1102 | // All extended points are now normalized, but the type 1103 | // doesn't encode this fact. Let us offer affine points 1104 | // to the caller. 1105 | 1106 | v.iter().map(|p| AffinePoint { u: p.u, v: p.v }) 1107 | } 1108 | 1109 | impl<'a, 'b> Mul<&'b Fr> for &'a AffinePoint { 1110 | type Output = ExtendedPoint; 1111 | 1112 | fn mul(self, other: &'b Fr) -> ExtendedPoint { 1113 | self.to_niels().multiply(&other.to_bytes()) 1114 | } 1115 | } 1116 | 1117 | impl_binops_multiplicative_mixed!(AffinePoint, Fr, ExtendedPoint); 1118 | 1119 | /// This represents a point in the prime-order subgroup of Jubjub, in extended 1120 | /// coordinates. 1121 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 1122 | pub struct SubgroupPoint(ExtendedPoint); 1123 | 1124 | impl From for ExtendedPoint { 1125 | fn from(val: SubgroupPoint) -> ExtendedPoint { 1126 | val.0 1127 | } 1128 | } 1129 | 1130 | impl<'a> From<&'a SubgroupPoint> for &'a ExtendedPoint { 1131 | fn from(val: &'a SubgroupPoint) -> &'a ExtendedPoint { 1132 | &val.0 1133 | } 1134 | } 1135 | 1136 | impl fmt::Display for SubgroupPoint { 1137 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 1138 | write!(f, "{}", self.0) 1139 | } 1140 | } 1141 | 1142 | impl ConditionallySelectable for SubgroupPoint { 1143 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 1144 | SubgroupPoint(ExtendedPoint::conditional_select(&a.0, &b.0, choice)) 1145 | } 1146 | } 1147 | 1148 | impl SubgroupPoint { 1149 | /// Constructs an AffinePoint given `u` and `v` without checking that the point is on 1150 | /// the curve or in the prime-order subgroup. 1151 | /// 1152 | /// This should only be used for hard-coding constants (e.g. fixed generators); in all 1153 | /// other cases, use [`SubgroupPoint::from_bytes`] instead. 1154 | /// 1155 | /// [`SubgroupPoint::from_bytes`]: SubgroupPoint#impl-GroupEncoding 1156 | pub const fn from_raw_unchecked(u: Fq, v: Fq) -> Self { 1157 | SubgroupPoint(AffinePoint::from_raw_unchecked(u, v).to_extended()) 1158 | } 1159 | } 1160 | 1161 | impl Sum for SubgroupPoint 1162 | where 1163 | T: Borrow, 1164 | { 1165 | fn sum(iter: I) -> Self 1166 | where 1167 | I: Iterator, 1168 | { 1169 | iter.fold(Self::identity(), |acc, item| acc + item.borrow()) 1170 | } 1171 | } 1172 | 1173 | impl Neg for SubgroupPoint { 1174 | type Output = SubgroupPoint; 1175 | 1176 | #[inline] 1177 | fn neg(self) -> SubgroupPoint { 1178 | SubgroupPoint(-self.0) 1179 | } 1180 | } 1181 | 1182 | impl Neg for &SubgroupPoint { 1183 | type Output = SubgroupPoint; 1184 | 1185 | #[inline] 1186 | fn neg(self) -> SubgroupPoint { 1187 | SubgroupPoint(-self.0) 1188 | } 1189 | } 1190 | 1191 | impl<'a, 'b> Add<&'b SubgroupPoint> for &'a ExtendedPoint { 1192 | type Output = ExtendedPoint; 1193 | 1194 | #[inline] 1195 | fn add(self, other: &'b SubgroupPoint) -> ExtendedPoint { 1196 | self + other.0 1197 | } 1198 | } 1199 | 1200 | impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a ExtendedPoint { 1201 | type Output = ExtendedPoint; 1202 | 1203 | #[inline] 1204 | fn sub(self, other: &'b SubgroupPoint) -> ExtendedPoint { 1205 | self - other.0 1206 | } 1207 | } 1208 | 1209 | impl_binops_additive!(ExtendedPoint, SubgroupPoint); 1210 | 1211 | impl<'a, 'b> Add<&'b SubgroupPoint> for &'a SubgroupPoint { 1212 | type Output = SubgroupPoint; 1213 | 1214 | #[inline] 1215 | fn add(self, other: &'b SubgroupPoint) -> SubgroupPoint { 1216 | SubgroupPoint(self.0 + other.0) 1217 | } 1218 | } 1219 | 1220 | impl<'a, 'b> Sub<&'b SubgroupPoint> for &'a SubgroupPoint { 1221 | type Output = SubgroupPoint; 1222 | 1223 | #[inline] 1224 | fn sub(self, other: &'b SubgroupPoint) -> SubgroupPoint { 1225 | SubgroupPoint(self.0 - other.0) 1226 | } 1227 | } 1228 | 1229 | impl_binops_additive!(SubgroupPoint, SubgroupPoint); 1230 | 1231 | impl<'a, 'b> Mul<&'b Fr> for &'a SubgroupPoint { 1232 | type Output = SubgroupPoint; 1233 | 1234 | fn mul(self, other: &'b Fr) -> SubgroupPoint { 1235 | SubgroupPoint(self.0.multiply(&other.to_bytes())) 1236 | } 1237 | } 1238 | 1239 | impl_binops_multiplicative!(SubgroupPoint, Fr); 1240 | 1241 | impl Group for ExtendedPoint { 1242 | type Scalar = Fr; 1243 | 1244 | fn random(mut rng: impl RngCore) -> Self { 1245 | loop { 1246 | let v = Fq::random(&mut rng); 1247 | let flip_sign = rng.next_u32() % 2 != 0; 1248 | 1249 | // See AffinePoint::from_bytes for details. 1250 | let v2 = v.square(); 1251 | let p = ((v2 - Fq::one()) 1252 | * ((Fq::one() + EDWARDS_D * v2).invert().unwrap_or(Fq::zero()))) 1253 | .sqrt() 1254 | .map(|u| AffinePoint { 1255 | u: if flip_sign { -u } else { u }, 1256 | v, 1257 | }); 1258 | 1259 | if p.is_some().into() { 1260 | let p = p.unwrap().to_curve(); 1261 | 1262 | if bool::from(!p.is_identity()) { 1263 | return p; 1264 | } 1265 | } 1266 | } 1267 | } 1268 | 1269 | fn identity() -> Self { 1270 | Self::identity() 1271 | } 1272 | 1273 | fn generator() -> Self { 1274 | AffinePoint::generator().into() 1275 | } 1276 | 1277 | fn is_identity(&self) -> Choice { 1278 | self.is_identity() 1279 | } 1280 | 1281 | #[must_use] 1282 | fn double(&self) -> Self { 1283 | self.double() 1284 | } 1285 | } 1286 | 1287 | impl Group for SubgroupPoint { 1288 | type Scalar = Fr; 1289 | 1290 | fn random(mut rng: impl RngCore) -> Self { 1291 | loop { 1292 | let p = ExtendedPoint::random(&mut rng).clear_cofactor(); 1293 | 1294 | if bool::from(!p.is_identity()) { 1295 | return p; 1296 | } 1297 | } 1298 | } 1299 | 1300 | fn identity() -> Self { 1301 | SubgroupPoint(ExtendedPoint::identity()) 1302 | } 1303 | 1304 | fn generator() -> Self { 1305 | ExtendedPoint::generator().clear_cofactor() 1306 | } 1307 | 1308 | fn is_identity(&self) -> Choice { 1309 | self.0.is_identity() 1310 | } 1311 | 1312 | #[must_use] 1313 | fn double(&self) -> Self { 1314 | SubgroupPoint(self.0.double()) 1315 | } 1316 | } 1317 | 1318 | #[cfg(feature = "alloc")] 1319 | impl WnafGroup for ExtendedPoint { 1320 | fn recommended_wnaf_for_num_scalars(num_scalars: usize) -> usize { 1321 | // Copied from bls12_381::g1, should be updated. 1322 | const RECOMMENDATIONS: [usize; 12] = 1323 | [1, 3, 7, 20, 43, 120, 273, 563, 1630, 3128, 7933, 62569]; 1324 | 1325 | let mut ret = 4; 1326 | for r in &RECOMMENDATIONS { 1327 | if num_scalars > *r { 1328 | ret += 1; 1329 | } else { 1330 | break; 1331 | } 1332 | } 1333 | 1334 | ret 1335 | } 1336 | } 1337 | 1338 | impl PrimeGroup for SubgroupPoint {} 1339 | 1340 | impl CofactorGroup for ExtendedPoint { 1341 | type Subgroup = SubgroupPoint; 1342 | 1343 | fn clear_cofactor(&self) -> Self::Subgroup { 1344 | SubgroupPoint(self.mul_by_cofactor()) 1345 | } 1346 | 1347 | fn into_subgroup(self) -> CtOption { 1348 | CtOption::new(SubgroupPoint(self), self.is_torsion_free()) 1349 | } 1350 | 1351 | fn is_torsion_free(&self) -> Choice { 1352 | self.is_torsion_free() 1353 | } 1354 | } 1355 | 1356 | impl Curve for ExtendedPoint { 1357 | type AffineRepr = AffinePoint; 1358 | 1359 | fn batch_normalize(p: &[Self], q: &mut [Self::AffineRepr]) { 1360 | Self::batch_normalize(p, q); 1361 | } 1362 | 1363 | fn to_affine(&self) -> Self::AffineRepr { 1364 | self.into() 1365 | } 1366 | } 1367 | 1368 | impl CofactorCurve for ExtendedPoint { 1369 | type Affine = AffinePoint; 1370 | } 1371 | 1372 | impl CofactorCurveAffine for AffinePoint { 1373 | type Scalar = Fr; 1374 | type Curve = ExtendedPoint; 1375 | 1376 | fn identity() -> Self { 1377 | Self::identity() 1378 | } 1379 | 1380 | fn generator() -> Self { 1381 | // The point with the lowest positive v-coordinate and positive u-coordinate. 1382 | AffinePoint { 1383 | u: Fq::from_raw([ 1384 | 0xe4b3_d35d_f1a7_adfe, 1385 | 0xcaf5_5d1b_29bf_81af, 1386 | 0x8b0f_03dd_d60a_8187, 1387 | 0x62ed_cbb8_bf37_87c8, 1388 | ]), 1389 | v: Fq::from_raw([ 1390 | 0x0000_0000_0000_000b, 1391 | 0x0000_0000_0000_0000, 1392 | 0x0000_0000_0000_0000, 1393 | 0x0000_0000_0000_0000, 1394 | ]), 1395 | } 1396 | } 1397 | 1398 | fn is_identity(&self) -> Choice { 1399 | self.is_identity() 1400 | } 1401 | 1402 | fn to_curve(&self) -> Self::Curve { 1403 | (*self).into() 1404 | } 1405 | } 1406 | 1407 | impl GroupEncoding for ExtendedPoint { 1408 | type Repr = [u8; 32]; 1409 | 1410 | fn from_bytes(bytes: &Self::Repr) -> CtOption { 1411 | AffinePoint::from_bytes(*bytes).map(Self::from) 1412 | } 1413 | 1414 | fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { 1415 | // We can't avoid curve checks when parsing a compressed encoding. 1416 | AffinePoint::from_bytes(*bytes).map(Self::from) 1417 | } 1418 | 1419 | fn to_bytes(&self) -> Self::Repr { 1420 | AffinePoint::from(self).to_bytes() 1421 | } 1422 | } 1423 | 1424 | impl GroupEncoding for SubgroupPoint { 1425 | type Repr = [u8; 32]; 1426 | 1427 | fn from_bytes(bytes: &Self::Repr) -> CtOption { 1428 | ExtendedPoint::from_bytes(bytes).and_then(|p| p.into_subgroup()) 1429 | } 1430 | 1431 | fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { 1432 | ExtendedPoint::from_bytes_unchecked(bytes).map(SubgroupPoint) 1433 | } 1434 | 1435 | fn to_bytes(&self) -> Self::Repr { 1436 | self.0.to_bytes() 1437 | } 1438 | } 1439 | 1440 | impl GroupEncoding for AffinePoint { 1441 | type Repr = [u8; 32]; 1442 | 1443 | fn from_bytes(bytes: &Self::Repr) -> CtOption { 1444 | Self::from_bytes(*bytes) 1445 | } 1446 | 1447 | fn from_bytes_unchecked(bytes: &Self::Repr) -> CtOption { 1448 | Self::from_bytes(*bytes) 1449 | } 1450 | 1451 | fn to_bytes(&self) -> Self::Repr { 1452 | self.to_bytes() 1453 | } 1454 | } 1455 | 1456 | #[test] 1457 | fn test_is_on_curve_var() { 1458 | assert!(AffinePoint::identity().is_on_curve_vartime()); 1459 | } 1460 | 1461 | #[test] 1462 | fn test_d_is_non_quadratic_residue() { 1463 | assert!(bool::from(EDWARDS_D.sqrt().is_none())); 1464 | assert!(bool::from((-EDWARDS_D).sqrt().is_none())); 1465 | assert!(bool::from((-EDWARDS_D).invert().unwrap().sqrt().is_none())); 1466 | } 1467 | 1468 | #[test] 1469 | fn test_affine_niels_point_identity() { 1470 | assert_eq!( 1471 | AffineNielsPoint::identity().v_plus_u, 1472 | AffinePoint::identity().to_niels().v_plus_u 1473 | ); 1474 | assert_eq!( 1475 | AffineNielsPoint::identity().v_minus_u, 1476 | AffinePoint::identity().to_niels().v_minus_u 1477 | ); 1478 | assert_eq!( 1479 | AffineNielsPoint::identity().t2d, 1480 | AffinePoint::identity().to_niels().t2d 1481 | ); 1482 | } 1483 | 1484 | #[test] 1485 | fn test_extended_niels_point_identity() { 1486 | assert_eq!( 1487 | ExtendedNielsPoint::identity().v_plus_u, 1488 | ExtendedPoint::identity().to_niels().v_plus_u 1489 | ); 1490 | assert_eq!( 1491 | ExtendedNielsPoint::identity().v_minus_u, 1492 | ExtendedPoint::identity().to_niels().v_minus_u 1493 | ); 1494 | assert_eq!( 1495 | ExtendedNielsPoint::identity().z, 1496 | ExtendedPoint::identity().to_niels().z 1497 | ); 1498 | assert_eq!( 1499 | ExtendedNielsPoint::identity().t2d, 1500 | ExtendedPoint::identity().to_niels().t2d 1501 | ); 1502 | } 1503 | 1504 | #[test] 1505 | fn test_assoc() { 1506 | let p = ExtendedPoint::from(AffinePoint { 1507 | u: Fq::from_raw([ 1508 | 0x81c5_71e5_d883_cfb0, 1509 | 0x049f_7a68_6f14_7029, 1510 | 0xf539_c860_bc3e_a21f, 1511 | 0x4284_715b_7ccc_8162, 1512 | ]), 1513 | v: Fq::from_raw([ 1514 | 0xbf09_6275_684b_b8ca, 1515 | 0xc7ba_2458_90af_256d, 1516 | 0x5911_9f3e_8638_0eb0, 1517 | 0x3793_de18_2f9f_b1d2, 1518 | ]), 1519 | }) 1520 | .mul_by_cofactor(); 1521 | assert!(p.is_on_curve_vartime()); 1522 | 1523 | assert_eq!( 1524 | (p * Fr::from(1000u64)) * Fr::from(3938u64), 1525 | p * (Fr::from(1000u64) * Fr::from(3938u64)), 1526 | ); 1527 | } 1528 | 1529 | #[test] 1530 | fn test_batch_normalize() { 1531 | let mut p = ExtendedPoint::from(AffinePoint { 1532 | u: Fq::from_raw([ 1533 | 0x81c5_71e5_d883_cfb0, 1534 | 0x049f_7a68_6f14_7029, 1535 | 0xf539_c860_bc3e_a21f, 1536 | 0x4284_715b_7ccc_8162, 1537 | ]), 1538 | v: Fq::from_raw([ 1539 | 0xbf09_6275_684b_b8ca, 1540 | 0xc7ba_2458_90af_256d, 1541 | 0x5911_9f3e_8638_0eb0, 1542 | 0x3793_de18_2f9f_b1d2, 1543 | ]), 1544 | }) 1545 | .mul_by_cofactor(); 1546 | 1547 | let mut v = vec![]; 1548 | for _ in 0..10 { 1549 | v.push(p); 1550 | p = p.double(); 1551 | } 1552 | 1553 | for p in &v { 1554 | assert!(p.is_on_curve_vartime()); 1555 | } 1556 | 1557 | let expected: std::vec::Vec<_> = v.iter().map(|p| AffinePoint::from(*p)).collect(); 1558 | let mut result0 = vec![AffinePoint::identity(); v.len()]; 1559 | ExtendedPoint::batch_normalize(&v, &mut result0); 1560 | for i in 0..10 { 1561 | assert!(expected[i] == result0[i]); 1562 | } 1563 | let result1: std::vec::Vec<_> = batch_normalize(&mut v).collect(); 1564 | for i in 0..10 { 1565 | assert!(expected[i] == result1[i]); 1566 | assert!(v[i].is_on_curve_vartime()); 1567 | assert!(AffinePoint::from(v[i]) == expected[i]); 1568 | } 1569 | let result2: std::vec::Vec<_> = batch_normalize(&mut v).collect(); 1570 | for i in 0..10 { 1571 | assert!(expected[i] == result2[i]); 1572 | assert!(v[i].is_on_curve_vartime()); 1573 | assert!(AffinePoint::from(v[i]) == expected[i]); 1574 | } 1575 | } 1576 | 1577 | #[cfg(test)] 1578 | const FULL_GENERATOR: AffinePoint = AffinePoint::from_raw_unchecked( 1579 | Fq::from_raw([ 1580 | 0xe4b3_d35d_f1a7_adfe, 1581 | 0xcaf5_5d1b_29bf_81af, 1582 | 0x8b0f_03dd_d60a_8187, 1583 | 0x62ed_cbb8_bf37_87c8, 1584 | ]), 1585 | Fq::from_raw([0xb, 0x0, 0x0, 0x0]), 1586 | ); 1587 | 1588 | #[cfg(test)] 1589 | const EIGHT_TORSION: [AffinePoint; 8] = [ 1590 | AffinePoint::from_raw_unchecked( 1591 | Fq::from_raw([ 1592 | 0xd92e_6a79_2720_0d43, 1593 | 0x7aa4_1ac4_3dae_8582, 1594 | 0xeaaa_e086_a166_18d1, 1595 | 0x71d4_df38_ba9e_7973, 1596 | ]), 1597 | Fq::from_raw([ 1598 | 0xff0d_2068_eff4_96dd, 1599 | 0x9106_ee90_f384_a4a1, 1600 | 0x16a1_3035_ad4d_7266, 1601 | 0x4958_bdb2_1966_982e, 1602 | ]), 1603 | ), 1604 | AffinePoint::from_raw_unchecked( 1605 | Fq::from_raw([ 1606 | 0xfffe_ffff_0000_0001, 1607 | 0x67ba_a400_89fb_5bfe, 1608 | 0xa5e8_0b39_939e_d334, 1609 | 0x73ed_a753_299d_7d47, 1610 | ]), 1611 | Fq::from_raw([0x0, 0x0, 0x0, 0x0]), 1612 | ), 1613 | AffinePoint::from_raw_unchecked( 1614 | Fq::from_raw([ 1615 | 0xd92e_6a79_2720_0d43, 1616 | 0x7aa4_1ac4_3dae_8582, 1617 | 0xeaaa_e086_a166_18d1, 1618 | 0x71d4_df38_ba9e_7973, 1619 | ]), 1620 | Fq::from_raw([ 1621 | 0x00f2_df96_100b_6924, 1622 | 0xc2b6_b572_0c79_b75d, 1623 | 0x1c98_a7d2_5c54_659e, 1624 | 0x2a94_e9a1_1036_e51a, 1625 | ]), 1626 | ), 1627 | AffinePoint::from_raw_unchecked( 1628 | Fq::from_raw([0x0, 0x0, 0x0, 0x0]), 1629 | Fq::from_raw([ 1630 | 0xffff_ffff_0000_0000, 1631 | 0x53bd_a402_fffe_5bfe, 1632 | 0x3339_d808_09a1_d805, 1633 | 0x73ed_a753_299d_7d48, 1634 | ]), 1635 | ), 1636 | AffinePoint::from_raw_unchecked( 1637 | Fq::from_raw([ 1638 | 0x26d1_9585_d8df_f2be, 1639 | 0xd919_893e_c24f_d67c, 1640 | 0x488e_f781_683b_bf33, 1641 | 0x0218_c81a_6eff_03d4, 1642 | ]), 1643 | Fq::from_raw([ 1644 | 0x00f2_df96_100b_6924, 1645 | 0xc2b6_b572_0c79_b75d, 1646 | 0x1c98_a7d2_5c54_659e, 1647 | 0x2a94_e9a1_1036_e51a, 1648 | ]), 1649 | ), 1650 | AffinePoint::from_raw_unchecked( 1651 | Fq::from_raw([ 1652 | 0x0001_0000_0000_0000, 1653 | 0xec03_0002_7603_0000, 1654 | 0x8d51_ccce_7603_04d0, 1655 | 0x0, 1656 | ]), 1657 | Fq::from_raw([0x0, 0x0, 0x0, 0x0]), 1658 | ), 1659 | AffinePoint::from_raw_unchecked( 1660 | Fq::from_raw([ 1661 | 0x26d1_9585_d8df_f2be, 1662 | 0xd919_893e_c24f_d67c, 1663 | 0x488e_f781_683b_bf33, 1664 | 0x0218_c81a_6eff_03d4, 1665 | ]), 1666 | Fq::from_raw([ 1667 | 0xff0d_2068_eff4_96dd, 1668 | 0x9106_ee90_f384_a4a1, 1669 | 0x16a1_3035_ad4d_7266, 1670 | 0x4958_bdb2_1966_982e, 1671 | ]), 1672 | ), 1673 | AffinePoint::from_raw_unchecked( 1674 | Fq::from_raw([0x0, 0x0, 0x0, 0x0]), 1675 | Fq::from_raw([0x1, 0x0, 0x0, 0x0]), 1676 | ), 1677 | ]; 1678 | 1679 | #[test] 1680 | fn find_eight_torsion() { 1681 | let g = ExtendedPoint::from(FULL_GENERATOR); 1682 | assert!(!bool::from(g.is_small_order())); 1683 | let g = g.multiply(&FR_MODULUS_BYTES); 1684 | assert!(bool::from(g.is_small_order())); 1685 | 1686 | let mut cur = g; 1687 | 1688 | for (i, point) in EIGHT_TORSION.iter().enumerate() { 1689 | let tmp = AffinePoint::from(cur); 1690 | if &tmp != point { 1691 | panic!("{}th torsion point should be {:?}", i, tmp); 1692 | } 1693 | 1694 | cur += &g; 1695 | } 1696 | } 1697 | 1698 | #[test] 1699 | fn find_curve_generator() { 1700 | let mut trial_bytes = [0; 32]; 1701 | for _ in 0..255 { 1702 | let a = AffinePoint::from_bytes(trial_bytes); 1703 | if bool::from(a.is_some()) { 1704 | let a = a.unwrap(); 1705 | assert!(a.is_on_curve_vartime()); 1706 | let b = ExtendedPoint::from(a); 1707 | let b = b.multiply(&FR_MODULUS_BYTES); 1708 | assert!(bool::from(b.is_small_order())); 1709 | let b = b.double(); 1710 | assert!(bool::from(b.is_small_order())); 1711 | let b = b.double(); 1712 | assert!(bool::from(b.is_small_order())); 1713 | if !bool::from(b.is_identity()) { 1714 | let b = b.double(); 1715 | assert!(bool::from(b.is_small_order())); 1716 | assert!(bool::from(b.is_identity())); 1717 | assert_eq!(FULL_GENERATOR, a); 1718 | assert_eq!(AffinePoint::generator(), a); 1719 | assert!(bool::from(a.mul_by_cofactor().is_torsion_free())); 1720 | return; 1721 | } 1722 | } 1723 | 1724 | trial_bytes[0] += 1; 1725 | } 1726 | 1727 | panic!("should have found a generator of the curve"); 1728 | } 1729 | 1730 | #[test] 1731 | fn test_small_order() { 1732 | for point in EIGHT_TORSION.iter() { 1733 | assert!(bool::from(point.is_small_order())); 1734 | } 1735 | } 1736 | 1737 | #[test] 1738 | fn test_is_identity() { 1739 | let a = EIGHT_TORSION[0].mul_by_cofactor(); 1740 | let b = EIGHT_TORSION[1].mul_by_cofactor(); 1741 | 1742 | assert_eq!(a.u, b.u); 1743 | assert_eq!(a.v, a.z); 1744 | assert_eq!(b.v, b.z); 1745 | assert!(a.v != b.v); 1746 | assert!(a.z != b.z); 1747 | 1748 | assert!(bool::from(a.is_identity())); 1749 | assert!(bool::from(b.is_identity())); 1750 | 1751 | for point in EIGHT_TORSION.iter() { 1752 | assert!(bool::from(point.mul_by_cofactor().is_identity())); 1753 | } 1754 | } 1755 | 1756 | #[test] 1757 | fn test_mul_consistency() { 1758 | let a = Fr([ 1759 | 0x21e6_1211_d993_4f2e, 1760 | 0xa52c_058a_693c_3e07, 1761 | 0x9ccb_77bf_b12d_6360, 1762 | 0x07df_2470_ec94_398e, 1763 | ]); 1764 | let b = Fr([ 1765 | 0x0333_6d1c_be19_dbe0, 1766 | 0x0153_618f_6156_a536, 1767 | 0x2604_c9e1_fc3c_6b15, 1768 | 0x04ae_581c_eb02_8720, 1769 | ]); 1770 | let c = Fr([ 1771 | 0xd7ab_f5bb_2468_3f4c, 1772 | 0x9d77_12cc_274b_7c03, 1773 | 0x9732_93db_9683_789f, 1774 | 0x0b67_7e29_380a_97a7, 1775 | ]); 1776 | assert_eq!(a * b, c); 1777 | let p = ExtendedPoint::from(AffinePoint { 1778 | u: Fq::from_raw([ 1779 | 0x81c5_71e5_d883_cfb0, 1780 | 0x049f_7a68_6f14_7029, 1781 | 0xf539_c860_bc3e_a21f, 1782 | 0x4284_715b_7ccc_8162, 1783 | ]), 1784 | v: Fq::from_raw([ 1785 | 0xbf09_6275_684b_b8ca, 1786 | 0xc7ba_2458_90af_256d, 1787 | 0x5911_9f3e_8638_0eb0, 1788 | 0x3793_de18_2f9f_b1d2, 1789 | ]), 1790 | }) 1791 | .mul_by_cofactor(); 1792 | assert_eq!(p * c, (p * a) * b); 1793 | 1794 | // Test Mul implemented on ExtendedNielsPoint 1795 | assert_eq!(p * c, (p.to_niels() * a) * b); 1796 | assert_eq!(p.to_niels() * c, (p * a) * b); 1797 | assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b); 1798 | 1799 | // Test Mul implemented on AffineNielsPoint 1800 | let p_affine_niels = AffinePoint::from(p).to_niels(); 1801 | assert_eq!(p * c, (p_affine_niels * a) * b); 1802 | assert_eq!(p_affine_niels * c, (p * a) * b); 1803 | assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b); 1804 | } 1805 | 1806 | #[test] 1807 | fn test_serialization_consistency() { 1808 | let gen = FULL_GENERATOR.mul_by_cofactor(); 1809 | let mut p = gen; 1810 | 1811 | let v = vec![ 1812 | [ 1813 | 203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110, 170, 185, 179, 108, 97, 1814 | 63, 13, 211, 247, 120, 79, 219, 110, 234, 131, 123, 19, 215, 1815 | ], 1816 | [ 1817 | 113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222, 233, 195, 203, 195, 1818 | 167, 129, 89, 121, 240, 142, 51, 166, 64, 250, 184, 202, 154, 177, 1819 | ], 1820 | [ 1821 | 197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149, 141, 240, 29, 14, 82, 1822 | 86, 225, 126, 129, 186, 157, 148, 162, 219, 51, 156, 199, 1823 | ], 1824 | [ 1825 | 182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97, 200, 139, 192, 83, 1826 | 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177, 19, 179, 0, 1827 | ], 1828 | [ 1829 | 118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38, 214, 176, 250, 4, 1830 | 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177, 230, 185, 1831 | ], 1832 | [ 1833 | 226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82, 174, 113, 112, 166, 1834 | 138, 171, 166, 113, 52, 251, 129, 197, 138, 45, 195, 7, 61, 140, 1835 | ], 1836 | [ 1837 | 38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135, 204, 215, 0, 33, 1838 | 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207, 12, 16, 24, 1839 | ], 1840 | [ 1841 | 17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85, 59, 86, 3, 226, 211, 1842 | 67, 179, 29, 238, 181, 102, 142, 58, 63, 57, 89, 174, 138, 1843 | ], 1844 | [ 1845 | 210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8, 140, 142, 216, 93, 1846 | 190, 183, 116, 174, 63, 33, 242, 177, 118, 148, 40, 241, 203, 1847 | ], 1848 | [ 1849 | 0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121, 185, 29, 152, 14, 1850 | 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53, 54, 68, 1851 | ], 1852 | [ 1853 | 178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56, 81, 239, 155, 122, 1854 | 59, 188, 237, 253, 11, 169, 208, 236, 12, 4, 163, 211, 88, 97, 1855 | ], 1856 | [ 1857 | 246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115, 144, 12, 90, 150, 1858 | 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67, 75, 57, 67, 1859 | ], 1860 | [ 1861 | 212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248, 22, 201, 208, 166, 1862 | 2, 83, 134, 130, 85, 129, 166, 136, 185, 191, 163, 38, 54, 10, 1863 | ], 1864 | [ 1865 | 8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19, 219, 143, 64, 161, 1866 | 99, 199, 77, 39, 148, 70, 213, 246, 227, 150, 178, 237, 178, 1867 | ], 1868 | [ 1869 | 11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84, 157, 214, 167, 73, 1870 | 233, 115, 81, 124, 134, 15, 31, 181, 60, 184, 130, 175, 159, 1871 | ], 1872 | [ 1873 | 141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80, 247, 9, 107, 124, 0, 1874 | 26, 203, 16, 237, 34, 214, 147, 133, 15, 29, 236, 37, 88, 1875 | ], 1876 | ]; 1877 | 1878 | let batched = AffinePoint::batch_from_bytes(v.iter().cloned()); 1879 | 1880 | for (expected_serialized, batch_deserialized) in v.into_iter().zip(batched.into_iter()) { 1881 | assert!(p.is_on_curve_vartime()); 1882 | let affine = AffinePoint::from(p); 1883 | let serialized = affine.to_bytes(); 1884 | let deserialized = AffinePoint::from_bytes(serialized).unwrap(); 1885 | assert_eq!(affine, deserialized); 1886 | assert_eq!(affine, batch_deserialized.unwrap()); 1887 | assert_eq!(expected_serialized, serialized); 1888 | p += gen; 1889 | } 1890 | } 1891 | 1892 | #[test] 1893 | fn test_zip_216() { 1894 | const NON_CANONICAL_ENCODINGS: [[u8; 32]; 2] = [ 1895 | // (0, 1) with sign bit set to 1. 1896 | [ 1897 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1898 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 1899 | 0x00, 0x00, 0x00, 0x80, 1900 | ], 1901 | // (0, -1) with sign bit set to 1. 1902 | [ 1903 | 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0x02, 0xa4, 1904 | 0xbd, 0x53, 0x05, 0xd8, 0xa1, 0x09, 0x08, 0xd8, 0x39, 0x33, 0x48, 0x7d, 0x9d, 0x29, 1905 | 0x53, 0xa7, 0xed, 0xf3, 1906 | ], 1907 | ]; 1908 | 1909 | for b in &NON_CANONICAL_ENCODINGS { 1910 | { 1911 | let mut encoding = *b; 1912 | 1913 | // The normal API should reject the non-canonical encoding. 1914 | assert!(bool::from(AffinePoint::from_bytes(encoding).is_none())); 1915 | 1916 | // If we clear the sign bit of the non-canonical encoding, it should be 1917 | // accepted by the normal API. 1918 | encoding[31] &= 0b0111_1111; 1919 | assert!(bool::from(AffinePoint::from_bytes(encoding).is_some())); 1920 | } 1921 | 1922 | { 1923 | // The bug-preserving API should accept the non-canonical encoding, and the 1924 | // resulting point should serialize to a different (canonical) encoding. 1925 | let parsed = AffinePoint::from_bytes_pre_zip216_compatibility(*b).unwrap(); 1926 | let mut encoded = parsed.to_bytes(); 1927 | assert_ne!(b, &encoded); 1928 | 1929 | // If we set the sign bit of the serialized encoding, it should match the 1930 | // non-canonical encoding. 1931 | encoded[31] |= 0b1000_0000; 1932 | assert_eq!(b, &encoded); 1933 | } 1934 | } 1935 | } 1936 | --------------------------------------------------------------------------------