├── .github └── workflows │ ├── cross.yml │ ├── curve25519-dalek.yml │ ├── ed25519-dalek.yml │ ├── workspace.yml │ └── x25519-dalek.yml ├── .gitignore ├── .typos.toml ├── CONTRIBUTING.md ├── Cargo.toml ├── README.md ├── curve25519-dalek-derive ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── src │ └── lib.rs └── tests │ └── tests.rs ├── curve25519-dalek ├── ACKNOWLEDGEMENTS.md ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── benches │ └── dalek_benchmarks.rs ├── build.rs ├── docs │ ├── assets │ │ └── rustdoc-include-katex-header.html │ ├── avx2-notes.md │ ├── ifma-notes.md │ └── parallel-formulas.md ├── src │ ├── backend │ │ ├── mod.rs │ │ ├── serial │ │ │ ├── curve_models │ │ │ │ └── mod.rs │ │ │ ├── fiat_u32 │ │ │ │ ├── field.rs │ │ │ │ └── mod.rs │ │ │ ├── fiat_u64 │ │ │ │ ├── field.rs │ │ │ │ └── mod.rs │ │ │ ├── mod.rs │ │ │ ├── scalar_mul │ │ │ │ ├── mod.rs │ │ │ │ ├── pippenger.rs │ │ │ │ ├── precomputed_straus.rs │ │ │ │ ├── straus.rs │ │ │ │ ├── variable_base.rs │ │ │ │ └── vartime_double_base.rs │ │ │ ├── u32 │ │ │ │ ├── constants.rs │ │ │ │ ├── field.rs │ │ │ │ ├── mod.rs │ │ │ │ └── scalar.rs │ │ │ └── u64 │ │ │ │ ├── constants.rs │ │ │ │ ├── field.rs │ │ │ │ ├── mod.rs │ │ │ │ └── scalar.rs │ │ └── vector │ │ │ ├── avx2 │ │ │ ├── constants.rs │ │ │ ├── edwards.rs │ │ │ ├── field.rs │ │ │ └── mod.rs │ │ │ ├── ifma │ │ │ ├── constants.rs │ │ │ ├── edwards.rs │ │ │ ├── field.rs │ │ │ └── mod.rs │ │ │ ├── mod.rs │ │ │ ├── packed_simd.rs │ │ │ └── scalar_mul │ │ │ ├── mod.rs │ │ │ ├── pippenger.rs │ │ │ ├── precomputed_straus.rs │ │ │ ├── straus.rs │ │ │ ├── variable_base.rs │ │ │ └── vartime_double_base.rs │ ├── constants.rs │ ├── diagnostics.rs │ ├── edwards.rs │ ├── edwards │ │ └── affine.rs │ ├── field.rs │ ├── lib.rs │ ├── lizard │ │ ├── README.md │ │ ├── jacobi_quartic.rs │ │ ├── lizard_constants.rs │ │ ├── lizard_ristretto.rs │ │ ├── mod.rs │ │ ├── u32_constants.rs │ │ └── u64_constants.rs │ ├── macros.rs │ ├── montgomery.rs │ ├── ristretto │ │ ├── elligator.rs │ │ └── mod.rs │ ├── scalar.rs │ ├── traits.rs │ └── window.rs ├── tests │ └── build_tests.sh └── vendor │ └── ristretto.sage ├── docs └── assets │ ├── dalek-logo-clear.png │ ├── dalek-logo.png │ └── dalek-logo.svg ├── ed25519-dalek ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── TESTVECTORS ├── VALIDATIONVECTORS ├── benches │ └── ed25519_benchmarks.rs ├── docs │ └── assets │ │ ├── ed25519-malleability.png │ │ └── rustdoc-include-katex-header.html ├── src │ ├── batch.rs │ ├── batch │ │ ├── strobe.rs │ │ └── transcript.rs │ ├── constants.rs │ ├── context.rs │ ├── errors.rs │ ├── hazmat.rs │ ├── lib.rs │ ├── signature.rs │ ├── signing.rs │ ├── verifying.rs │ └── verifying │ │ └── stream.rs └── tests │ ├── ed25519.rs │ ├── examples │ ├── pkcs8-v1.der │ ├── pkcs8-v2.der │ └── pubkey.der │ ├── pkcs8.rs │ ├── validation_criteria.rs │ └── x25519.rs └── x25519-dalek ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── x25519.rs ├── docs └── assets │ └── rustdoc-include-katex-header.html ├── res └── bubblesort-zines-secret-messages-cover.jpeg ├── src ├── lib.rs └── x25519.rs └── tests └── x25519_tests.rs /.github/workflows/cross.yml: -------------------------------------------------------------------------------- 1 | name: Cross 2 | 3 | on: 4 | push: 5 | branches: [ '**' ] 6 | pull_request: 7 | branches: [ '**' ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUSTFLAGS: '-D warnings' 12 | 13 | jobs: 14 | 15 | test-cross: 16 | name: Test 17 | strategy: 18 | matrix: 19 | include: 20 | # ARM32 21 | - target: armv7-unknown-linux-gnueabihf 22 | rust: stable 23 | 24 | # ARM64 25 | - target: aarch64-unknown-linux-gnu 26 | rust: stable 27 | 28 | # PPC32 29 | - target: powerpc-unknown-linux-gnu 30 | rust: stable 31 | 32 | # TODO: We only test x/ed/curve for cross as derive is platform specifics 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v3 36 | - run: ${{ matrix.deps }} 37 | - uses: dtolnay/rust-toolchain@master 38 | with: 39 | toolchain: ${{ matrix.rust }} 40 | targets: ${{ matrix.target }} 41 | - uses: RustCrypto/actions/cross-install@master 42 | - run: cross test -p curve25519-dalek --release --target ${{ matrix.target }} 43 | - run: cross test -p ed25519-dalek --release --target ${{ matrix.target }} 44 | - run: cross test -p x25519-dalek --release --target ${{ matrix.target }} 45 | -------------------------------------------------------------------------------- /.github/workflows/curve25519-dalek.yml: -------------------------------------------------------------------------------- 1 | name: curve25519 Rust 2 | 3 | on: 4 | push: 5 | branches: [ '**' ] 6 | paths: 7 | - 'curve25519-dalek/**' 8 | - '.github/workflows/curve25519-dalek.yml' 9 | pull_request: 10 | branches: [ '**' ] 11 | paths: 12 | - 'curve25519-dalek/**' 13 | - '.github/workflows/curve25519-dalek.yml' 14 | 15 | defaults: 16 | run: 17 | working-directory: curve25519-dalek 18 | 19 | env: 20 | CARGO_TERM_COLOR: always 21 | RUSTFLAGS: '-D warnings' 22 | 23 | jobs: 24 | 25 | test-fiat: 26 | name: Test fiat backend 27 | runs-on: ubuntu-latest 28 | strategy: 29 | matrix: 30 | include: 31 | # 32-bit target 32 | - target: i686-unknown-linux-gnu 33 | deps: sudo apt update && sudo apt install gcc-multilib 34 | 35 | # 64-bit target 36 | - target: x86_64-unknown-linux-gnu 37 | steps: 38 | - uses: actions/checkout@v3 39 | - uses: dtolnay/rust-toolchain@stable 40 | - run: rustup target add ${{ matrix.target }} 41 | - run: ${{ matrix.deps }} 42 | - env: 43 | RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"' 44 | run: cargo test --target ${{ matrix.target }} 45 | 46 | # Default no_std test only tests using serial across all crates 47 | build-nostd-fiat: 48 | name: Build fiat on no_std target (thumbv7em-none-eabi) 49 | runs-on: ubuntu-latest 50 | strategy: 51 | matrix: 52 | include: 53 | - crate: curve25519-dalek 54 | steps: 55 | - uses: actions/checkout@v3 56 | - uses: dtolnay/rust-toolchain@master 57 | with: 58 | toolchain: stable 59 | targets: thumbv7em-none-eabi 60 | - uses: taiki-e/install-action@cargo-hack 61 | # No default features build 62 | - name: no_std fiat / no feat ${{ matrix.crate }} 63 | env: 64 | RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"' 65 | run: cargo build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --no-default-features 66 | - name: no_std fiat / cargo hack ${{ matrix.crate }} 67 | env: 68 | RUSTFLAGS: '--cfg curve25519_dalek_backend="fiat"' 69 | run: cargo hack build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std,getrandom 70 | 71 | test-serial: 72 | name: Test serial backend 73 | runs-on: ubuntu-latest 74 | strategy: 75 | matrix: 76 | include: 77 | # 32-bit target 78 | - target: i686-unknown-linux-gnu 79 | deps: sudo apt update && sudo apt install gcc-multilib 80 | 81 | # 64-bit target 82 | - target: x86_64-unknown-linux-gnu 83 | steps: 84 | - uses: actions/checkout@v3 85 | - uses: dtolnay/rust-toolchain@stable 86 | - run: rustup target add ${{ matrix.target }} 87 | - run: ${{ matrix.deps }} 88 | - env: 89 | RUSTFLAGS: '--cfg curve25519_dalek_backend="serial"' 90 | run: cargo test --target ${{ matrix.target }} 91 | 92 | build-script: 93 | name: Test Build Script 94 | runs-on: ubuntu-latest 95 | steps: 96 | - uses: actions/checkout@v3 97 | - uses: dtolnay/rust-toolchain@master 98 | with: 99 | toolchain: stable 100 | targets: wasm32-unknown-unknown,x86_64-unknown-linux-gnu,i686-unknown-linux-gnu 101 | - run: bash tests/build_tests.sh 102 | 103 | test-simd-nightly: 104 | name: Test simd backend (nightly) 105 | runs-on: ubuntu-latest 106 | steps: 107 | - uses: actions/checkout@v3 108 | - uses: dtolnay/rust-toolchain@nightly 109 | - env: 110 | # This will: 111 | # 1) build all of the x86_64 SIMD code, 112 | # 2) run all of the SIMD-specific tests that the test runner supports, 113 | # 3) run all of the normal tests using the best available SIMD backend. 114 | # This should automatically pick up the simd backend in a x84_64 runner 115 | RUSTFLAGS: '-C target_cpu=native' 116 | run: cargo test --target x86_64-unknown-linux-gnu 117 | 118 | test-simd-stable: 119 | name: Test simd backend (stable) 120 | runs-on: ubuntu-latest 121 | steps: 122 | - uses: actions/checkout@v3 123 | - uses: dtolnay/rust-toolchain@stable 124 | - env: 125 | # This will run AVX2-specific tests and run all of the normal tests 126 | # with the AVX2 backend, even if the runner supports AVX512. 127 | # This should automatically pick up the simd backend in a x86_64 runner 128 | # It should pick AVX2 due to stable toolchain used since AVX512 requires nigthly 129 | RUSTFLAGS: '-C target_feature=+avx2' 130 | run: cargo test --no-default-features --features alloc,precomputed-tables,zeroize,group-bits --target x86_64-unknown-linux-gnu 131 | 132 | msrv: 133 | name: Current MSRV is 1.85.0 134 | runs-on: ubuntu-latest 135 | steps: 136 | - uses: actions/checkout@v3 137 | # Re-resolve Cargo.lock with minimal versions 138 | - uses: dtolnay/rust-toolchain@nightly 139 | - run: cargo update -Z minimal-versions 140 | # Now check that `cargo build` works with respect to the oldest possible 141 | # deps and the stated MSRV 142 | - uses: dtolnay/rust-toolchain@1.85.0 143 | - run: cargo build --no-default-features --features serde 144 | # Also make sure the AVX2 build works 145 | - run: cargo build --target x86_64-unknown-linux-gnu 146 | -------------------------------------------------------------------------------- /.github/workflows/ed25519-dalek.yml: -------------------------------------------------------------------------------- 1 | name: ed25519 Rust 2 | 3 | on: 4 | push: 5 | branches: [ '**' ] 6 | paths: 'ed25519-dalek/**' 7 | pull_request: 8 | branches: [ '**' ] 9 | paths: 'ed25519-dalek/**' 10 | 11 | defaults: 12 | run: 13 | working-directory: ed25519-dalek 14 | 15 | env: 16 | CARGO_TERM_COLOR: always 17 | RUSTFLAGS: '-D warnings' 18 | RUSTDOCFLAGS: '-D warnings' 19 | 20 | jobs: 21 | 22 | msrv: 23 | name: Current MSRV is 1.85.0 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | # Re-resolve Cargo.lock with minimal versions 28 | - uses: dtolnay/rust-toolchain@nightly 29 | - run: cargo update -Z minimal-versions 30 | # Now check that `cargo build` works with respect to the oldest possible 31 | # deps and the stated MSRV 32 | - uses: dtolnay/rust-toolchain@1.85.0 33 | - run: cargo build 34 | -------------------------------------------------------------------------------- /.github/workflows/workspace.yml: -------------------------------------------------------------------------------- 1 | name: All 2 | 3 | on: 4 | push: 5 | branches: [ '**' ] 6 | pull_request: 7 | branches: [ '**' ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | RUSTFLAGS: '-D warnings' 12 | 13 | jobs: 14 | test-stable: 15 | name: Test 32/64 bit stable 16 | runs-on: ubuntu-latest 17 | strategy: 18 | matrix: 19 | include: 20 | # 32-bit target 21 | - target: i686-unknown-linux-gnu 22 | deps: sudo apt update && sudo apt install gcc-multilib 23 | 24 | # 64-bit target 25 | - target: x86_64-unknown-linux-gnu 26 | steps: 27 | - uses: actions/checkout@v3 28 | - uses: dtolnay/rust-toolchain@stable 29 | - run: rustup target add ${{ matrix.target }} 30 | - run: ${{ matrix.deps }} 31 | - run: cargo test --target ${{ matrix.target }} --no-default-features 32 | - run: cargo test --target ${{ matrix.target }} 33 | - run: cargo test --target ${{ matrix.target }} --all-features 34 | 35 | test-nightly: 36 | name: Test Nightly 37 | runs-on: ubuntu-latest 38 | steps: 39 | - uses: actions/checkout@v3 40 | - uses: dtolnay/rust-toolchain@nightly 41 | - run: cargo test 42 | 43 | bench: 44 | name: Check that benchmarks compile 45 | runs-on: ubuntu-latest 46 | steps: 47 | - uses: actions/checkout@v3 48 | - uses: dtolnay/rust-toolchain@stable 49 | - name: Build u32 bench 50 | env: 51 | RUSTFLAGS: '--cfg curve25519_dalek_bits="32"' 52 | run: cargo build --benches 53 | - name: Build u64 bench 54 | env: 55 | RUSTFLAGS: '--cfg curve25519_dalek_bits="64"' 56 | run: cargo build --benches 57 | - name: Build default (host native) bench 58 | run: cargo build --benches 59 | 60 | # Test no_std with serial (default) 61 | build-nostd-serial: 62 | name: Build serial on no_std target (thumbv7em-none-eabi) 63 | runs-on: ubuntu-latest 64 | strategy: 65 | matrix: 66 | include: 67 | - crate: curve25519-dalek 68 | - crate: ed25519-dalek 69 | - crate: x25519-dalek 70 | steps: 71 | - uses: actions/checkout@v3 72 | - uses: dtolnay/rust-toolchain@master 73 | with: 74 | toolchain: stable 75 | targets: thumbv7em-none-eabi 76 | - uses: taiki-e/install-action@cargo-hack 77 | # No default features build 78 | - name: no_std / no feat ${{ matrix.crate }} 79 | run: cargo build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --no-default-features 80 | - name: no_std / cargo hack ${{ matrix.crate }} 81 | run: cargo hack build -p ${{ matrix.crate }} --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std,os_rng 82 | 83 | clippy: 84 | name: Check that clippy is happy 85 | runs-on: ubuntu-latest 86 | steps: 87 | - uses: actions/checkout@v3 88 | - uses: dtolnay/rust-toolchain@1.87.0 89 | with: 90 | components: clippy 91 | - run: cargo clippy --target x86_64-unknown-linux-gnu --all-features 92 | 93 | rustfmt: 94 | name: Check formatting 95 | runs-on: ubuntu-latest 96 | steps: 97 | - uses: actions/checkout@v3 98 | - uses: dtolnay/rust-toolchain@stable 99 | with: 100 | components: rustfmt 101 | - run: cargo fmt --all -- --check 102 | 103 | doc: 104 | name: Check docs 105 | runs-on: ubuntu-latest 106 | steps: 107 | - uses: actions/checkout@v3 108 | - uses: dtolnay/rust-toolchain@stable 109 | with: 110 | toolchain: stable 111 | - run: cargo doc --all-features 112 | 113 | typos: 114 | name: Check for typos 115 | runs-on: ubuntu-latest 116 | steps: 117 | - uses: actions/checkout@v4 118 | - uses: crate-ci/typos@v1.33.1 119 | -------------------------------------------------------------------------------- /.github/workflows/x25519-dalek.yml: -------------------------------------------------------------------------------- 1 | name: x25519 Rust 2 | 3 | on: 4 | push: 5 | branches: [ '**' ] 6 | paths: 'x25519-dalek/**' 7 | pull_request: 8 | branches: [ '**' ] 9 | paths: 'x25519-dalek/**' 10 | 11 | defaults: 12 | run: 13 | working-directory: x25519-dalek 14 | 15 | env: 16 | CARGO_TERM_COLOR: always 17 | RUSTFLAGS: '-D warnings' 18 | RUSTDOCFLAGS: '-D warnings' 19 | 20 | jobs: 21 | 22 | msrv: 23 | name: Current MSRV is 1.85.0 24 | runs-on: ubuntu-latest 25 | steps: 26 | - uses: actions/checkout@v3 27 | # Re-resolve Cargo.lock with minimal versions 28 | - uses: dtolnay/rust-toolchain@nightly 29 | - run: cargo update -Z minimal-versions 30 | # Now check that `cargo build` works with respect to the oldest possible 31 | # deps and the stated MSRV 32 | - uses: dtolnay/rust-toolchain@1.85.0 33 | - run: cargo build 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */target/* 2 | target 3 | Cargo.lock 4 | */Cargo.lock 5 | build*.txt 6 | *~ 7 | \#* 8 | .\#* 9 | *.swp 10 | *.orig 11 | *.bak 12 | 13 | *.s 14 | -------------------------------------------------------------------------------- /.typos.toml: -------------------------------------------------------------------------------- 1 | [files] 2 | extend-exclude = [ 3 | ".git/", 4 | "*.svg", 5 | "TESTVECTORS" 6 | ] 7 | 8 | [default] 9 | extend-ignore-re = [ 10 | # Patterns which appear to be 36 or more characters of Base64/Base64url 11 | '\b[0-9A-Za-z+/]{36,}\b', 12 | '\b[0-9A-Za-z_-]{36,}\b', 13 | ] 14 | 15 | [default.extend-words] 16 | "AKE" = "AKE" # Authenticated Key Exchange 17 | "Ono" = "Ono" # Surname 18 | "Tung" = "Tung" # Name 19 | "Monts" = "Monts" # Abbreviation for Montgomery 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to curve25519-dalek 2 | 3 | If you have questions or comments, please feel free to email the 4 | authors. 5 | 6 | For feature requests, suggestions, and bug reports, please open an issue on 7 | [our Github](https://github.com/dalek-cryptography/curve25519-dalek). (Or, send us 8 | an email if you're opposed to using Github for whatever reason.) 9 | 10 | Patches are welcomed as pull requests on 11 | [our Github](https://github.com/dalek-cryptography/curve25519-dalek), as well as by 12 | email (preferably sent to all of the authors listed in `Cargo.toml`). 13 | 14 | All issues on curve25519-dalek are mentored, if you want help with a bug just 15 | ask @rozbb or @tarcieri. 16 | 17 | Some issues are easier than others. The `easy` label can be used to find the 18 | easy issues. If you want to work on an issue, please leave a comment so that we 19 | can assign it to you! 20 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "curve25519-dalek", 4 | "curve25519-dalek-derive", 5 | "ed25519-dalek", 6 | "x25519-dalek" 7 | ] 8 | resolver = "2" 9 | 10 | [profile.dev] 11 | opt-level = 2 12 | 13 | [patch.crates-io] 14 | curve25519-dalek-derive = { path = "./curve25519-dalek-derive" } 15 | curve25519-dalek = { path = "./curve25519-dalek" } 16 | x25519-dalek = { path = "./x25519-dalek" } 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | dalek-cryptography logo: a dalek with edwards curves as sparkles coming out of its radar-schnozzley blaster thingies 6 |

7 | 8 | # Dalek elliptic curve cryptography 9 | 10 | This repo contains pure-Rust crates for elliptic curve cryptography: 11 | 12 | | Crate | Description | Crates.io | Docs | CI | 13 | -------------------------------------------|----------------|-----------|------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 14 | | [`curve25519‑dalek`](./curve25519-dalek) | A library for arithmetic over the Curve25519 and Ristretto elliptic curves and their associated scalars. | [![](https://img.shields.io/crates/v/curve25519-dalek.svg)](https://crates.io/crates/curve25519-dalek) | [![](https://img.shields.io/docsrs/curve25519-dalek)](https://docs.rs/curve25519-dalek) | [![CI](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/curve25519-dalek.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/curve25519-dalek.yml) | 15 | | [`ed25519‑dalek`](./ed25519-dalek) | An implementation of the EdDSA digital signature scheme over Curve25519. | [![](https://img.shields.io/crates/v/ed25519-dalek.svg)](https://crates.io/crates/ed25519-dalek) | [![](https://docs.rs/ed25519-dalek/badge.svg)](https://docs.rs/ed25519-dalek) | [![CI](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/ed25519-dalek.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/ed25519-dalek.yml) | 16 | | [`x25519‑dalek`](./x25519-dalek) | An implementation of elliptic curve Diffie-Hellman key exchange over Curve25519. | [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) | [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) | [![CI](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/x25519-dalek.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/x25519-dalek.yml) | 17 | 18 | There is also the [`curve25519-dalek-derive`](./curve25519-dalek-derive) crate, which is just a helper crate with some macros that make curve25519-dalek easier to write. 19 | 20 | # Contributing 21 | 22 | Please see [`CONTRIBUTING.md`](./CONTRIBUTING.md). 23 | 24 | # Code of Conduct 25 | 26 | We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html), 27 | with the following additional clauses: 28 | 29 | * We respect the rights to privacy and anonymity for contributors and people in 30 | the community. If someone wishes to contribute under a pseudonym different to 31 | their primary identity, that wish is to be respected by all contributors. 32 | -------------------------------------------------------------------------------- /curve25519-dalek-derive/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Entries are listed in reverse chronological order per undeprecated 4 | major series. 5 | 6 | ### 0.1.1 7 | 8 | * Copied over license files from [original](https://github.com/koute/unsafe_target_feature/tree/389ae00d34cf0ff589cb8d9b38a85ae1b05ebfdc) repo 9 | -------------------------------------------------------------------------------- /curve25519-dalek-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "curve25519-dalek-derive" 3 | version = "0.1.1" 4 | edition = "2021" 5 | 6 | repository = "https://github.com/dalek-cryptography/curve25519-dalek" 7 | homepage = "https://github.com/dalek-cryptography/curve25519-dalek" 8 | documentation = "https://docs.rs/curve25519-dalek-derive" 9 | license = "MIT OR Apache-2.0" 10 | readme = "README.md" 11 | description = "curve25519-dalek Derives" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | proc-macro2 = "1.0.66" 18 | quote = "1.0.31" 19 | syn = { version = "2.0.27", features = ["full"] } 20 | -------------------------------------------------------------------------------- /curve25519-dalek-derive/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 | -------------------------------------------------------------------------------- /curve25519-dalek-derive/README.md: -------------------------------------------------------------------------------- 1 | # A more convenient `#[target_feature]` replacement 2 | 3 | To get good performance out of SIMD everything on the SIMD codepath must be inlined. 4 | With how SIMD is currently implemented in Rust one of two things have to be true for 5 | a function using SIMD to be inlinable: (and this includes the SIMD intrinsics themselves) 6 | 7 | a) The whole program has to be compiled with the relevant `-C target-cpu` or `-C target-feature` flags. 8 | 9 | b) SIMD support must be automatically detected at runtime, and every function on the SIMD codepath must be marked with `#[target_feature]`. 10 | 11 | Both have their downsides. Setting the `target-cpu` or `target-features` makes the resulting binary 12 | incompatible with older CPUs, while using `#[target_feature]` is incredibly inconvenient. 13 | 14 | This crate is meant to make `#[target_feature]` less painful to use. 15 | 16 | ## Problems with `#[target_feature]` 17 | 18 | When we're not compiling with the relevant `target-cpu`/`target-feature` flags everything on 19 | the SIMD codepath must be marked with the `#[target_feature]` attribute. This is not a problem 20 | when all of your SIMD code is neatly encapsulated inside of a single function, but once you start 21 | to build out more elaborate abstractions it starts to become painful to use. 22 | 23 | * It can only be used on `unsafe` functions, so everything on your SIMD codepath now has to be `unsafe`. 24 | 25 | In theory this is nice - these functions require the relevant SIMD instructions to be present at runtime, 26 | so calling them without checking is obviously unsafe! But in practice this is rarely what you want. When 27 | you build an abstraction over SIMD code you usually want to assume that *internally* within your module 28 | all of the necessary SIMD instructions are available, and you only want to check this at the boundaries 29 | when you're first entering your module. You do *not* want to infect everything *inside* of the module with 30 | `unsafe` since you've already checked this invariant at the module's API boundary. 31 | 32 | * It cannot be used on non-`unsafe` trait methods. 33 | 34 | If you're implementing a trait, say for example `std::ops::Add`, then you cannot mark the method `unsafe` 35 | unless the original trait also has it marked as `unsafe`, and usually it doesn't. 36 | 37 | * It makes it impossible to abstract over a given SIMD instruction set using a trait. 38 | 39 | For example, let's assume you want to abstract over which SIMD instructions you use using a trait in the following way: 40 | 41 | ```rust 42 | trait Backend { 43 | unsafe fn sum(input: &[u32]) -> u32; 44 | } 45 | 46 | struct AVX; 47 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 48 | impl Backend for AVX { 49 | #[target_feature(enable = "avx")] 50 | unsafe fn sum(xs: &[u32]) -> u32 { 51 | // ... 52 | todo!(); 53 | } 54 | } 55 | 56 | struct AVX2; 57 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 58 | impl Backend for AVX2 { 59 | #[target_feature(enable = "avx2")] 60 | unsafe fn sum(xs: &[u32]) -> u32 { 61 | // ... 62 | todo!(); 63 | } 64 | } 65 | 66 | // And now you want a have function which calls into that trait: 67 | unsafe fn do_calculations(xs: &[u32]) -> u32 where B: Backend { 68 | let value = B::sum(xs); 69 | // ...do some more calculations here... 70 | value 71 | } 72 | ``` 73 | 74 | We have a problem here. This has to be marked with `#[target_feature]`, and that has to specify the concrete 75 | feature flag for a given SIMD instruction set, but this function is generic so we can't do that! 76 | 77 | ## How does this crate make it better? 78 | 79 | ### You can now mark safe functions with `#[target_feature]` 80 | 81 | This crate exposes an `#[unsafe_target_feature]` macro which works just like `#[target_feature]` except 82 | it moves the `unsafe` from the function prototype into the macro name, and can be used on safe functions. 83 | 84 | ```rust 85 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 86 | #[target_feature(enable = "avx2")] 87 | unsafe fn func() {} 88 | ``` 89 | 90 | ```rust 91 | use curve25519_dalek_derive::unsafe_target_feature; 92 | 93 | // No `unsafe` on the function itself! 94 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 95 | #[unsafe_target_feature("avx2")] 96 | fn func() {} 97 | ``` 98 | 99 | It can also be used to mark functions inside of impls: 100 | 101 | ```rust,compile_fail 102 | struct S; 103 | 104 | impl core::ops::Add for S { 105 | type Output = S; 106 | // ERROR: method `add` has an incompatible type for trait 107 | #[target_feature(enable = "avx2")] 108 | unsafe fn add(self, rhs: S) -> S { 109 | S 110 | } 111 | } 112 | ``` 113 | 114 | ```rust 115 | use curve25519_dalek_derive::unsafe_target_feature; 116 | 117 | struct S; 118 | 119 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 120 | #[unsafe_target_feature("avx2")] 121 | impl core::ops::Add for S { 122 | type Output = S; 123 | // No `unsafe` on the function itself! 124 | fn add(self, rhs: S) -> S { 125 | S 126 | } 127 | } 128 | 129 | ``` 130 | 131 | ### You can generate specialized copies of a module for each target feature 132 | 133 | ```rust 134 | use curve25519_dalek_derive::unsafe_target_feature_specialize; 135 | 136 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 137 | #[unsafe_target_feature_specialize("sse2", "avx2", conditional("avx512ifma", nightly))] 138 | mod simd { 139 | #[for_target_feature("sse2")] 140 | pub const CONSTANT: u32 = 1; 141 | 142 | #[for_target_feature("avx2")] 143 | pub const CONSTANT: u32 = 2; 144 | 145 | #[for_target_feature("avx512ifma")] 146 | pub const CONSTANT: u32 = 3; 147 | 148 | pub fn func() { /* ... */ } 149 | } 150 | 151 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 152 | fn entry_point() { 153 | #[cfg(nightly)] 154 | if std::is_x86_feature_detected!("avx512ifma") { 155 | return simd_avx512ifma::func(); 156 | } 157 | 158 | if std::is_x86_feature_detected!("avx2") { 159 | return simd_avx2::func(); 160 | } 161 | 162 | if std::is_x86_feature_detected!("sse2") { 163 | return simd_sse2::func(); 164 | } 165 | 166 | unimplemented!(); 167 | } 168 | ``` 169 | 170 | ## How to use `#[unsafe_target_feature]`? 171 | 172 | - Can be used on `fn`s, `impl`s and `mod`s. 173 | - When used on a function will only apply to that function; it won't apply to any nested functions, traits, mods, etc. 174 | - When used on an `impl` will only apply to all of the functions directly defined inside of that `impl`. 175 | - When used on a `mod` will only apply to all of the `fn`s and `impl`s directly defined inside of that `mod`. 176 | - Cannot be used on methods which use `self` or `Self`; instead use it on the `impl` in which the method is defined. 177 | 178 | ## License 179 | 180 | Licensed under either of 181 | 182 | * Apache License, Version 2.0, [LICENSE-APACHE](LICENSE-APACHE) 183 | * MIT license ([LICENSE-MIT](LICENSE-MIT)) 184 | 185 | at your option. 186 | 187 | ### Contribution 188 | 189 | Unless you explicitly state otherwise, any contribution intentionally submitted 190 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 191 | dual licensed as above, without any additional terms or conditions. 192 | -------------------------------------------------------------------------------- /curve25519-dalek-derive/tests/tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(any(target_arch = "x86", target_arch = "x86_64"))] 2 | #![allow(dead_code)] 3 | #![allow(unused_imports)] 4 | 5 | use curve25519_dalek_derive::{unsafe_target_feature, unsafe_target_feature_specialize}; 6 | 7 | #[unsafe_target_feature("sse2")] 8 | /// A doc comment. 9 | fn function(a: u32, b: u32) -> u32 { 10 | a - b 11 | } 12 | 13 | #[unsafe_target_feature("sse2")] 14 | fn function_with_const_arg(b: u32) -> u32 { 15 | N - b 16 | } 17 | 18 | #[unsafe_target_feature("sse2")] 19 | fn function_with_where_clause(a: T, b: T) -> T::Output 20 | where 21 | T: Copy + core::ops::Sub, 22 | { 23 | a - b 24 | } 25 | 26 | #[unsafe_target_feature("sse2")] 27 | #[rustfmt::skip] 28 | fn function_with_rustfmt_skip() {} 29 | 30 | struct Struct { 31 | a: u32, 32 | } 33 | 34 | #[unsafe_target_feature("sse2")] 35 | impl Struct { 36 | #[allow(unused_mut)] 37 | fn member_function(&self, mut b: u32) -> u32 { 38 | self.a - b 39 | } 40 | 41 | fn member_function_with_const_arg(self) -> u32 { 42 | self.a - N 43 | } 44 | } 45 | 46 | struct StructWithGenerics 47 | where 48 | T: Copy + core::ops::Sub, 49 | { 50 | a: T, 51 | } 52 | 53 | #[unsafe_target_feature("sse2")] 54 | impl StructWithGenerics 55 | where 56 | T: Copy + core::ops::Sub, 57 | { 58 | #[inline] 59 | fn member_function(&self, b: T) -> T::Output { 60 | self.a - b 61 | } 62 | } 63 | 64 | struct StructWithGenericsNoWhere { 65 | a: T, 66 | } 67 | 68 | #[unsafe_target_feature("sse2")] 69 | impl StructWithGenericsNoWhere { 70 | #[inline(always)] 71 | fn member_function(&self, b: T) -> T::Output { 72 | self.a - b 73 | } 74 | } 75 | 76 | #[unsafe_target_feature("sse2")] 77 | #[allow(dead_code)] 78 | impl<'a> From<&'a Struct> for () { 79 | fn from(_: &'a Struct) -> Self {} 80 | } 81 | 82 | #[unsafe_target_feature("sse2")] 83 | mod inner { 84 | fn inner_function(a: u32, b: u32) -> u32 { 85 | a - b 86 | } 87 | } 88 | 89 | #[unsafe_target_feature_specialize("sse2", "avx2")] 90 | mod inner_spec { 91 | #[for_target_feature("sse2")] 92 | const CONST: u32 = 1; 93 | 94 | #[for_target_feature("avx2")] 95 | const CONST: u32 = 2; 96 | 97 | pub fn spec_function(a: u32, b: u32) -> u32 { 98 | a - b - CONST 99 | } 100 | 101 | #[for_target_feature("sse2")] 102 | const IS_AVX2: bool = false; 103 | 104 | #[for_target_feature("avx2")] 105 | const IS_AVX2: bool = true; 106 | 107 | #[test] 108 | fn test_specialized() { 109 | assert!(!IS_AVX2); 110 | } 111 | 112 | #[cfg(test)] 113 | mod tests { 114 | #[test] 115 | fn test_specialized_inner() { 116 | assert!(!super::IS_AVX2); 117 | } 118 | } 119 | } 120 | 121 | #[unsafe_target_feature("sse2")] 122 | #[test] 123 | fn test_sse2_only() {} 124 | 125 | // it turns out that for compilation to succeed, the feature needs be supported by rustc. For this 126 | // test actually verify what happens when the target_feature is not enabled, this needs to be a 127 | // pretty esoteric feature. Looking at the table of supported avx512 features at 128 | // https://en.wikipedia.org/wiki/AVX-512#CPUs_with_AVX-512 it seems avx512vp2intersect is one of the 129 | // most unusual ones that has rustc knows about 130 | #[unsafe_target_feature("avx512vp2intersect")] 131 | #[test] 132 | fn test_unset_target_feature() { 133 | compile_error!("When an unknown target_feature is set on a test, unsafe_target_feature is expected remove the function"); 134 | } 135 | 136 | #[test] 137 | fn test_function() { 138 | assert_eq!(function(10, 3), 7); 139 | assert_eq!(function_with_where_clause(10, 3), 7); 140 | assert_eq!(function_with_const_arg::<10>(3), 7); 141 | assert_eq!(Struct { a: 10 }.member_function(3), 7); 142 | assert_eq!(StructWithGenerics { a: 10 }.member_function(3), 7); 143 | assert_eq!(StructWithGenericsNoWhere { a: 10 }.member_function(3), 7); 144 | assert_eq!(inner_spec_sse2::spec_function(10, 3), 6); 145 | assert_eq!(inner_spec_avx2::spec_function(10, 3), 5); 146 | } 147 | -------------------------------------------------------------------------------- /curve25519-dalek/ACKNOWLEDGEMENTS.md: -------------------------------------------------------------------------------- 1 | # Go ed25519 2 | 3 | Portions of curve25519-dalek were originally derived from Adam Langley's 4 | Go ed25519 implementation, found at , 5 | under the following licence: 6 | 7 | ``` 8 | Copyright (c) 2012 The Go Authors. All rights reserved. 9 | 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are 12 | met: 13 | 14 | * Redistributions of source code must retain the above copyright 15 | notice, this list of conditions and the following disclaimer. 16 | * Redistributions in binary form must reproduce the above 17 | copyright notice, this list of conditions and the following disclaimer 18 | in the documentation and/or other materials provided with the 19 | distribution. 20 | * Neither the name of Google Inc. nor the names of its 21 | contributors may be used to endorse or promote products derived from 22 | this software without specific prior written permission. 23 | 24 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 25 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 26 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 27 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 28 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 29 | EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 30 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 31 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 33 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 34 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | ``` 36 | 37 | # Lizard 38 | 39 | The `src/lizard` directory was copied from [Signal's curve25519-dalek repo]( https://github.com/signalapp/curve25519-dalek/tree/7c6d34756355a3566a704da84dce7b1c039a6572). Its license is copied below 40 | 41 | ``` 42 | MIT License 43 | 44 | Copyright (c) 2019 Bas Westerbaan 45 | 46 | Permission is hereby granted, free of charge, to any person obtaining a copy 47 | of this software and associated documentation files (the "Software"), to deal 48 | in the Software without restriction, including without limitation the rights 49 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 50 | copies of the Software, and to permit persons to whom the Software is 51 | furnished to do so, subject to the following conditions: 52 | 53 | The above copyright notice and this permission notice shall be included in all 54 | copies or substantial portions of the Software. 55 | 56 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 57 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 58 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 59 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 60 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 61 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 62 | SOFTWARE. 63 | ``` 64 | -------------------------------------------------------------------------------- /curve25519-dalek/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "curve25519-dalek" 3 | # Before incrementing: 4 | # - update CHANGELOG 5 | # - update README if required by semver 6 | # - if README was updated, also update module documentation in src/lib.rs 7 | version = "5.0.0-pre.1" 8 | edition = "2024" 9 | rust-version = "1.85.0" 10 | authors = [ 11 | "Isis Lovecruft ", 12 | "Henry de Valence ", 13 | ] 14 | readme = "README.md" 15 | license = "BSD-3-Clause" 16 | repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/curve25519-dalek" 17 | homepage = "https://github.com/dalek-cryptography/curve25519-dalek" 18 | documentation = "https://docs.rs/curve25519-dalek" 19 | categories = ["cryptography", "no-std"] 20 | keywords = ["cryptography", "crypto", "ristretto", "curve25519", "ristretto255"] 21 | description = "A pure-Rust implementation of group operations on ristretto255 and Curve25519" 22 | exclude = ["**/.gitignore", ".gitignore"] 23 | 24 | [package.metadata.docs.rs] 25 | rustdoc-args = [ 26 | "--html-in-header", 27 | "docs/assets/rustdoc-include-katex-header.html", 28 | "--cfg", 29 | "docsrs", 30 | ] 31 | features = [ 32 | "serde", 33 | "rand_core", 34 | "digest", 35 | "legacy_compatibility", 36 | "group-bits", 37 | ] 38 | 39 | [dev-dependencies] 40 | sha2 = { version = "0.11.0-rc.2", default-features = false } 41 | bincode = "1" 42 | criterion = { version = "0.5", features = ["html_reports"] } 43 | hex = "0.4.2" 44 | proptest = "1" 45 | rand = "0.9" 46 | rand_core = { version = "0.9", default-features = false, features = ["os_rng"] } 47 | 48 | [build-dependencies] 49 | rustc_version = "0.4.0" 50 | 51 | [[bench]] 52 | name = "dalek_benchmarks" 53 | harness = false 54 | required-features = ["alloc", "rand_core"] 55 | 56 | [dependencies] 57 | cfg-if = "1" 58 | ff = { version = "=0.14.0-pre.0", default-features = false, optional = true } 59 | group = { version = "=0.14.0-pre.0", default-features = false, optional = true } 60 | rand_core = { version = "0.9", default-features = false, optional = true } 61 | digest = { version = "0.11.0-rc.1", default-features = false, optional = true, features = [ 62 | "block-api", 63 | ] } 64 | subtle = { version = "2.6.0", default-features = false, features = [ 65 | "const-generics", 66 | ] } 67 | serde = { version = "1.0", default-features = false, optional = true, features = [ 68 | "derive", 69 | ] } 70 | zeroize = { version = "1", default-features = false, optional = true } 71 | 72 | [target.'cfg(target_arch = "x86_64")'.dependencies] 73 | cpufeatures = "0.2.17" 74 | 75 | [target.'cfg(curve25519_dalek_backend = "fiat")'.dependencies] 76 | fiat-crypto = { version = "0.3.0", default-features = false } 77 | 78 | [features] 79 | default = ["alloc", "precomputed-tables", "zeroize"] 80 | alloc = ["zeroize?/alloc"] 81 | precomputed-tables = [] 82 | legacy_compatibility = [] 83 | group = ["dep:group", "rand_core"] 84 | group-bits = ["group", "ff/bits"] 85 | digest = ["dep:digest"] 86 | lizard = ["digest"] 87 | 88 | [target.'cfg(all(not(curve25519_dalek_backend = "fiat"), not(curve25519_dalek_backend = "serial"), target_arch = "x86_64"))'.dependencies] 89 | curve25519-dalek-derive = "0.1" 90 | 91 | [lints.rust.unexpected_cfgs] 92 | level = "warn" 93 | check-cfg = [ 94 | 'cfg(allow_unused_unsafe)', 95 | 'cfg(curve25519_dalek_backend, values("fiat", "serial", "simd", "unstable_avx512"))', 96 | 'cfg(curve25519_dalek_diagnostics, values("build"))', 97 | 'cfg(curve25519_dalek_bits, values("32", "64"))', 98 | 'cfg(nightly)', 99 | ] 100 | -------------------------------------------------------------------------------- /curve25519-dalek/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2021 isis agora lovecruft. All rights reserved. 2 | Copyright (c) 2016-2021 Henry de Valence. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /curve25519-dalek/Makefile: -------------------------------------------------------------------------------- 1 | FEATURES := serde rand_core digest legacy_compatibility 2 | 3 | export RUSTDOCFLAGS := \ 4 | --cfg docsrs \ 5 | --html-in-header docs/assets/rustdoc-include-katex-header.html 6 | 7 | doc: 8 | cargo +nightly rustdoc --features "$(FEATURES)" 9 | 10 | doc-internal: 11 | cargo +nightly rustdoc --features "$(FEATURES)" -- --document-private-items 12 | -------------------------------------------------------------------------------- /curve25519-dalek/build.rs: -------------------------------------------------------------------------------- 1 | //! This selects the curve25519_dalek_bits either by default from target_pointer_width or explicitly set 2 | 3 | #![deny(clippy::unwrap_used, dead_code)] 4 | 5 | #[allow(non_camel_case_types)] 6 | #[derive(PartialEq, Debug)] 7 | enum DalekBits { 8 | Dalek32, 9 | Dalek64, 10 | } 11 | 12 | use std::fmt::Formatter; 13 | 14 | impl std::fmt::Display for DalekBits { 15 | fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { 16 | let w_bits = match self { 17 | DalekBits::Dalek32 => "32", 18 | DalekBits::Dalek64 => "64", 19 | }; 20 | write!(f, "{w_bits}") 21 | } 22 | } 23 | 24 | fn main() { 25 | let target_arch = match std::env::var("CARGO_CFG_TARGET_ARCH") { 26 | Ok(arch) => arch, 27 | _ => "".to_string(), 28 | }; 29 | 30 | let curve25519_dalek_bits = match std::env::var("CARGO_CFG_CURVE25519_DALEK_BITS").as_deref() { 31 | Ok("32") => DalekBits::Dalek32, 32 | Ok("64") => DalekBits::Dalek64, 33 | _ => deterministic::determine_curve25519_dalek_bits(&target_arch), 34 | }; 35 | 36 | println!("cargo:rustc-cfg=curve25519_dalek_bits=\"{curve25519_dalek_bits}\""); 37 | 38 | let nightly = if rustc_version::version_meta() 39 | .expect("failed to detect rustc version") 40 | .channel 41 | == rustc_version::Channel::Nightly 42 | { 43 | println!("cargo:rustc-cfg=nightly"); 44 | true 45 | } else { 46 | false 47 | }; 48 | 49 | let rustc_version = rustc_version::version().expect("failed to detect rustc version"); 50 | if rustc_version.major == 1 && rustc_version.minor <= 64 { 51 | // Old versions of Rust complain when you have an `unsafe fn` and you use `unsafe {}` inside, 52 | // so for those we want to apply the `#[allow(unused_unsafe)]` attribute to get rid of that warning. 53 | println!("cargo:rustc-cfg=allow_unused_unsafe"); 54 | } 55 | 56 | // Backend overrides / defaults 57 | let curve25519_dalek_backend = match std::env::var("CARGO_CFG_CURVE25519_DALEK_BACKEND") 58 | .as_deref() 59 | { 60 | Ok("fiat") => "fiat", 61 | Ok("serial") => "serial", 62 | Ok("simd") => { 63 | // simd can only be enabled on x86_64 & 64bit target_pointer_width 64 | match is_capable_simd(&target_arch, curve25519_dalek_bits) { 65 | true => "simd", 66 | // If override is not possible this must result to compile error 67 | // See: issues/532 68 | false => panic!("Could not override curve25519_dalek_backend to simd"), 69 | } 70 | } 71 | Ok("unstable_avx512") if nightly => { 72 | // simd can only be enabled on x86_64 & 64bit target_pointer_width 73 | match is_capable_simd(&target_arch, curve25519_dalek_bits) { 74 | true => { 75 | // In addition enable Avx2 fallback through simd stable backend 76 | // NOTE: Compiler permits duplicate / multi value on the same key 77 | println!("cargo:rustc-cfg=curve25519_dalek_backend=\"simd\""); 78 | 79 | "unstable_avx512" 80 | } 81 | // If override is not possible this must result to compile error 82 | // See: issues/532 83 | false => panic!("Could not override curve25519_dalek_backend to unstable_avx512"), 84 | } 85 | } 86 | Ok("unstable_avx512") if !nightly => { 87 | panic!( 88 | "Could not override curve25519_dalek_backend to unstable_avx512, as this is nightly only" 89 | ); 90 | } 91 | // default between serial / simd (if potentially capable) 92 | _ => match is_capable_simd(&target_arch, curve25519_dalek_bits) { 93 | true => "simd", 94 | false => "serial", 95 | }, 96 | }; 97 | println!("cargo:rustc-cfg=curve25519_dalek_backend=\"{curve25519_dalek_backend}\""); 98 | } 99 | 100 | // Is the target arch & curve25519_dalek_bits potentially simd capable ? 101 | fn is_capable_simd(arch: &str, bits: DalekBits) -> bool { 102 | arch == "x86_64" && bits == DalekBits::Dalek64 103 | } 104 | 105 | // Deterministic cfg(curve25519_dalek_bits) when this is not explicitly set. 106 | mod deterministic { 107 | 108 | use super::*; 109 | 110 | // Custom Rust non-cargo build tooling needs to set CARGO_CFG_TARGET_POINTER_WIDTH 111 | static ERR_MSG_NO_POINTER_WIDTH: &str = 112 | "Standard Cargo TARGET_POINTER_WIDTH environment variable is not set."; 113 | 114 | // When either non-32 or 64 TARGET_POINTER_WIDTH detected 115 | static ERR_MSG_UNKNOWN_POINTER_WIDTH: &str = "Unknown TARGET_POINTER_WIDTH detected."; 116 | 117 | // Warning when the curve25519_dalek_bits cannot be determined 118 | fn determine_curve25519_dalek_bits_warning(cause: &str) { 119 | println!("cargo:warning=\"Defaulting to curve25519_dalek_bits=32: {cause}\""); 120 | } 121 | 122 | // Determine the curve25519_dalek_bits based on Rust standard TARGET triplet 123 | pub(super) fn determine_curve25519_dalek_bits(target_arch: &String) -> DalekBits { 124 | let target_pointer_width = match std::env::var("CARGO_CFG_TARGET_POINTER_WIDTH") { 125 | Ok(pw) => pw, 126 | Err(_) => { 127 | determine_curve25519_dalek_bits_warning(ERR_MSG_NO_POINTER_WIDTH); 128 | return DalekBits::Dalek32; 129 | } 130 | }; 131 | 132 | #[allow(clippy::match_single_binding)] 133 | match &target_arch { 134 | //Issues: 449 and 456 135 | //TODO: When adding arch defaults use proper types not String match 136 | //TODO(Arm): Needs tests + benchmarks to back this up 137 | //TODO(Wasm32): Needs tests + benchmarks to back this up 138 | _ => match target_pointer_width.as_ref() { 139 | "64" => DalekBits::Dalek64, 140 | "32" => DalekBits::Dalek32, 141 | // Intended default solely for non-32/64 target pointer widths 142 | // Otherwise known target platforms only. 143 | _ => { 144 | determine_curve25519_dalek_bits_warning(ERR_MSG_UNKNOWN_POINTER_WIDTH); 145 | DalekBits::Dalek32 146 | } 147 | }, 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /curve25519-dalek/docs/assets/rustdoc-include-katex-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 13 | -------------------------------------------------------------------------------- /curve25519-dalek/docs/avx2-notes.md: -------------------------------------------------------------------------------- 1 | An AVX2 implementation of the vectorized point operation strategy. 2 | 3 | # Field element representation 4 | 5 | Our strategy is to implement 4-wide multiplication and squaring by 6 | wordslicing, using one 64-bit AVX2 lane for each field element. Field 7 | elements are represented in the usual way as 10 `u32` limbs in radix 8 | \\(25.5\\) (i.e., alternating between \\(2\^{26}\\) for even limbs and 9 | \\(2\^{25}\\) for odd limbs). This has the effect that passing between 10 | the parallel 32-bit AVX2 representation and the serial 64-bit 11 | representation (which uses radix \\(2^{51}\\)) amounts to regrouping 12 | digits. 13 | 14 | The field element representation is oriented around the AVX2 15 | `vpmuludq` instruction, which multiplies the low 32 bits of each 16 | 64-bit lane of each operand to produce a 64-bit result. 17 | 18 | ```text,no_run 19 | (a1 ?? b1 ?? c1 ?? d1 ??) 20 | (a2 ?? b2 ?? c2 ?? d2 ??) 21 | 22 | (a1*a2 b1*b2 c1*c2 d1*d2) 23 | ``` 24 | 25 | To unpack 32-bit values into 64-bit lanes for use in multiplication 26 | it would be convenient to use the `vpunpck[lh]dq` instructions, 27 | which unpack and interleave the low and high 32-bit lanes of two 28 | source vectors. 29 | However, the AVX2 versions of these instructions are designed to 30 | operate only within 128-bit lanes of the 256-bit vectors, so that 31 | interleaving the low lanes of `(a0 b0 c0 d0 a1 b1 c1 d1)` with zero 32 | gives `(a0 00 b0 00 a1 00 b1 00)`. Instead, we pre-shuffle the data 33 | layout as `(a0 b0 a1 b1 c0 d0 c1 d1)` so that we can unpack the 34 | "low" and "high" parts as 35 | 36 | ```text,no_run 37 | (a0 00 b0 00 c0 00 d0 00) 38 | (a1 00 b1 00 c1 00 d1 00) 39 | ``` 40 | 41 | The data layout for a vector of four field elements \\( (a,b,c,d) 42 | \\) with limbs \\( a_0, a_1, \ldots, a_9 \\) is as `[u32x8; 5]` in 43 | the form 44 | 45 | ```text,no_run 46 | (a0 b0 a1 b1 c0 d0 c1 d1) 47 | (a2 b2 a3 b3 c2 d2 c3 d3) 48 | (a4 b4 a5 b5 c4 d4 c5 d5) 49 | (a6 b6 a7 b7 c6 d6 c7 d7) 50 | (a8 b8 a9 b9 c8 d8 c9 d9) 51 | ``` 52 | 53 | Since this breaks cleanly into two 128-bit lanes, it may be possible 54 | to adapt it to 128-bit vector instructions such as NEON without too 55 | much difficulty. 56 | 57 | # Avoiding Overflow in Doubling 58 | 59 | To analyze the size of the field element coefficients during the 60 | computations, we can parameterize the bounds on the limbs of each 61 | field element by \\( b \in \mathbb R \\) representing the excess bits 62 | above that limb's radix, so that each limb is bounded by either 63 | \\(2\^{25+b} \\) or \\( 2\^{26+b} \\), as appropriate. 64 | 65 | The multiplication routine requires that its inputs are bounded with 66 | \\( b < 1.75 \\), in order to fit a multiplication by \\( 19 \\) 67 | into 32 bits. Since \\( \lg 19 < 4.25 \\), \\( 19x < 2\^{32} \\) 68 | when \\( x < 2\^{27.75} = 2\^{26 + 1.75} \\). However, this is only 69 | required for one of the inputs; the other can grow up to \\( b < 2.5 70 | \\). 71 | 72 | In addition, the multiplication and squaring routines do not 73 | canonically reduce their outputs, but can leave some small uncarried 74 | excesses, so that their reduced outputs are bounded with 75 | \\( b < 0.007 \\). 76 | 77 | The non-parallel portion of the doubling formulas is 78 | $$ 79 | \begin{aligned} 80 | (S\_5 &&,&& S\_6 &&,&& S\_8 &&,&& S\_9 ) 81 | &\gets 82 | (S\_1 + S\_2 &&,&& S\_1 - S\_2 &&,&& S\_1 + 2S\_3 - S\_2 &&,&& S\_1 + S\_2 - S\_4) 83 | \end{aligned} 84 | $$ 85 | 86 | Computing \\( (S\_5, S\_6, S\_8, S\_9 ) \\) as 87 | $$ 88 | \begin{matrix} 89 | & S\_1 & S\_1 & S\_1 & S\_1 \\\\ 90 | +& S\_2 & & & S\_2 \\\\ 91 | +& & & S\_3 & \\\\ 92 | +& & & S\_3 & \\\\ 93 | +& & 2p & 2p & 2p \\\\ 94 | -& & S\_2 & S\_2 & \\\\ 95 | -& & & & S\_4 \\\\ 96 | =& S\_5 & S\_6 & S\_8 & S\_9 97 | \end{matrix} 98 | $$ 99 | results in bit-excesses \\( < (1.01, 1.60, 2.33, 2.01)\\) for 100 | \\( (S\_5, S\_6, S\_8, S\_9 ) \\). The products we want to compute 101 | are then 102 | $$ 103 | \begin{aligned} 104 | X\_3 &\gets S\_8 S\_9 \leftrightarrow (2.33, 2.01) \\\\ 105 | Y\_3 &\gets S\_5 S\_6 \leftrightarrow (1.01, 1.60) \\\\ 106 | Z\_3 &\gets S\_8 S\_6 \leftrightarrow (2.33, 1.60) \\\\ 107 | T\_3 &\gets S\_5 S\_9 \leftrightarrow (1.01, 2.01) 108 | \end{aligned} 109 | $$ 110 | which are too large: it's not possible to arrange the multiplicands so 111 | that one vector has \\(b < 2.5\\) and the other has \\( b < 1.75 \\). 112 | However, if we flip the sign of \\( S\_4 = S\_0\^2 \\) during 113 | squaring, so that we output \\(S\_4' = -S\_4 \pmod p\\), then we can 114 | compute 115 | $$ 116 | \begin{matrix} 117 | & S\_1 & S\_1 & S\_1 & S\_1 \\\\ 118 | +& S\_2 & & & S\_2 \\\\ 119 | +& & & S\_3 & \\\\ 120 | +& & & S\_3 & \\\\ 121 | +& & & & S\_4' \\\\ 122 | +& & 2p & 2p & \\\\ 123 | -& & S\_2 & S\_2 & \\\\ 124 | =& S\_5 & S\_6 & S\_8 & S\_9 125 | \end{matrix} 126 | $$ 127 | resulting in bit-excesses \\( < (1.01, 1.60, 2.33, 1.60)\\) for 128 | \\( (S\_5, S\_6, S\_8, S\_9 ) \\). The products we want to compute 129 | are then 130 | $$ 131 | \begin{aligned} 132 | X\_3 &\gets S\_8 S\_9 \leftrightarrow (2.33, 1.60) \\\\ 133 | Y\_3 &\gets S\_5 S\_6 \leftrightarrow (1.01, 1.60) \\\\ 134 | Z\_3 &\gets S\_8 S\_6 \leftrightarrow (2.33, 1.60) \\\\ 135 | T\_3 &\gets S\_5 S\_9 \leftrightarrow (1.01, 1.60) 136 | \end{aligned} 137 | $$ 138 | whose right-hand sides are all bounded with \\( b < 1.75 \\) and 139 | whose left-hand sides are all bounded with \\( b < 2.5 \\), 140 | so that we can avoid any intermediate reductions. 141 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/fiat_u32/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - Isis Agora Lovecruft 9 | // - Henry de Valence 10 | 11 | //! The `u32` backend uses `u32`s and a `(u32, u32) -> u64` multiplier. 12 | //! 13 | //! This code is intended to be portable, but it requires that 14 | //! multiplication of two \\(32\\)-bit values to a \\(64\\)-bit result 15 | //! is constant-time on the target platform. 16 | //! 17 | //! This uses the formally-verified field arithmetic generated by the 18 | //! [fiat-crypto project](https://github.com/mit-plv/fiat-crypto) 19 | 20 | #[path = "../u32/scalar.rs"] 21 | pub mod scalar; 22 | 23 | pub mod field; 24 | 25 | #[path = "../u32/constants.rs"] 26 | pub mod constants; 27 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/fiat_u64/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2018 Isis Lovecruft, Henry de Valence 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - Isis Agora Lovecruft 9 | // - Henry de Valence 10 | 11 | //! The `u64` backend uses `u64`s and a `(u64, u64) -> u128` multiplier. 12 | //! 13 | //! On x86_64, the idiom `(x as u128) * (y as u128)` lowers to `MUL` 14 | //! instructions taking 64-bit inputs and producing 128-bit outputs. On 15 | //! other platforms, this implementation is not recommended. 16 | //! 17 | //! On Haswell and newer, the BMI2 extension provides `MULX`, and on 18 | //! Broadwell and newer, the ADX extension provides `ADCX` and `ADOX` 19 | //! (allowing the CPU to compute two carry chains in parallel). These 20 | //! will be used if available. 21 | 22 | #[path = "../u64/scalar.rs"] 23 | pub mod scalar; 24 | 25 | pub mod field; 26 | 27 | #[path = "../u64/constants.rs"] 28 | pub mod constants; 29 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | //! Serial implementations of field, scalar, point arithmetic. 13 | //! 14 | //! When the vector backend is disabled, the crate uses the mixed-model strategy 15 | //! for implementing point operations and scalar multiplication; see the 16 | //! [`curve_models`] and [`scalar_mul`] documentation for more information. 17 | //! 18 | //! When the vector backend is enabled, the field and scalar 19 | //! implementations are still used for non-vectorized operations. 20 | 21 | use cfg_if::cfg_if; 22 | 23 | cfg_if! { 24 | if #[cfg(curve25519_dalek_backend = "fiat")] { 25 | 26 | #[cfg(curve25519_dalek_bits = "32")] 27 | #[doc(hidden)] 28 | pub mod fiat_u32; 29 | 30 | #[cfg(curve25519_dalek_bits = "64")] 31 | #[doc(hidden)] 32 | pub mod fiat_u64; 33 | 34 | } else { 35 | 36 | #[cfg(curve25519_dalek_bits = "32")] 37 | #[doc(hidden)] 38 | pub mod u32; 39 | 40 | #[cfg(curve25519_dalek_bits = "64")] 41 | #[doc(hidden)] 42 | pub mod u64; 43 | 44 | } 45 | } 46 | 47 | pub mod curve_models; 48 | 49 | pub mod scalar_mul; 50 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/scalar_mul/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | //! Implementations of various scalar multiplication algorithms. 13 | //! 14 | //! Note that all of these implementations use serial code for field 15 | //! arithmetic with the multi-model strategy described in the 16 | //! `curve_models` module. The vectorized AVX2 backend has its own 17 | //! scalar multiplication implementations, since it only uses one 18 | //! curve model. 19 | 20 | #[allow(missing_docs)] 21 | pub mod variable_base; 22 | 23 | #[allow(missing_docs)] 24 | pub mod vartime_double_base; 25 | 26 | #[cfg(feature = "alloc")] 27 | pub mod straus; 28 | 29 | #[cfg(feature = "alloc")] 30 | pub mod precomputed_straus; 31 | 32 | #[cfg(feature = "alloc")] 33 | pub mod pippenger; 34 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/scalar_mul/pippenger.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2019 Oleg Andreev 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - Oleg Andreev 9 | 10 | //! Implementation of a variant of Pippenger's algorithm. 11 | 12 | #![allow(non_snake_case)] 13 | 14 | use alloc::vec::Vec; 15 | 16 | use core::borrow::Borrow; 17 | use core::cmp::Ordering; 18 | 19 | use crate::edwards::EdwardsPoint; 20 | use crate::scalar::Scalar; 21 | use crate::traits::VartimeMultiscalarMul; 22 | 23 | /// Implements a version of Pippenger's algorithm. 24 | /// 25 | /// The algorithm works as follows: 26 | /// 27 | /// Let `n` be a number of point-scalar pairs. 28 | /// Let `w` be a window of bits (6..8, chosen based on `n`, see cost factor). 29 | /// 30 | /// 1. Prepare `2^(w-1) - 1` buckets with indices `[1..2^(w-1))` initialized with identity points. 31 | /// Bucket 0 is not needed as it would contain points multiplied by 0. 32 | /// 2. Convert scalars to a radix-`2^w` representation with signed digits in `[-2^w/2, 2^w/2]`. 33 | /// Note: only the last digit may equal `2^w/2`. 34 | /// 3. Starting with the last window, for each point `i=[0..n)` add it to a bucket indexed by 35 | /// the point's scalar's value in the window. 36 | /// 4. Once all points in a window are sorted into buckets, add buckets by multiplying each 37 | /// by their index. Efficient way of doing it is to start with the last bucket and compute two sums: 38 | /// intermediate sum from the last to the first, and the full sum made of all intermediate sums. 39 | /// 5. Shift the resulting sum of buckets by `w` bits by using `w` doublings. 40 | /// 6. Add to the return value. 41 | /// 7. Repeat the loop. 42 | /// 43 | /// Approximate cost w/o wNAF optimizations (A = addition, D = doubling): 44 | /// 45 | /// ```ascii 46 | /// cost = (n*A + 2*(2^w/2)*A + w*D + A)*256/w 47 | /// | | | | | 48 | /// | | | | looping over 256/w windows 49 | /// | | | adding to the result 50 | /// sorting points | shifting the sum by w bits (to the next window, starting from last window) 51 | /// one by one | 52 | /// into buckets adding/subtracting all buckets 53 | /// multiplied by their indexes 54 | /// using a sum of intermediate sums 55 | /// ``` 56 | /// 57 | /// For large `n`, dominant factor is (n*256/w) additions. 58 | /// However, if `w` is too big and `n` is not too big, then `(2^w/2)*A` could dominate. 59 | /// Therefore, the optimal choice of `w` grows slowly as `n` grows. 60 | /// 61 | /// This algorithm is adapted from section 4 of . 62 | pub struct Pippenger; 63 | 64 | impl VartimeMultiscalarMul for Pippenger { 65 | type Point = EdwardsPoint; 66 | 67 | fn optional_multiscalar_mul(scalars: I, points: J) -> Option 68 | where 69 | I: IntoIterator, 70 | I::Item: Borrow, 71 | J: IntoIterator>, 72 | { 73 | use crate::traits::Identity; 74 | 75 | let mut scalars = scalars.into_iter(); 76 | let size = scalars.by_ref().size_hint().0; 77 | 78 | // Digit width in bits. As digit width grows, 79 | // number of point additions goes down, but amount of 80 | // buckets and bucket additions grows exponentially. 81 | let w = if size < 500 { 82 | 6 83 | } else if size < 800 { 84 | 7 85 | } else { 86 | 8 87 | }; 88 | 89 | let max_digit: usize = 1 << w; 90 | let digits_count: usize = Scalar::to_radix_2w_size_hint(w); 91 | let buckets_count: usize = max_digit / 2; // digits are signed+centered hence 2^w/2, excluding 0-th bucket 92 | 93 | // Collect optimized scalars and points in buffers for repeated access 94 | // (scanning the whole set per digit position). 95 | let scalars = scalars.map(|s| s.borrow().as_radix_2w(w)); 96 | 97 | let points = points 98 | .into_iter() 99 | .map(|p| p.map(|P| P.as_projective_niels())); 100 | 101 | let scalars_points = scalars 102 | .zip(points) 103 | .map(|(s, maybe_p)| maybe_p.map(|p| (s, p))) 104 | .collect::>>()?; 105 | 106 | // Prepare 2^w/2 buckets. 107 | // buckets[i] corresponds to a multiplication factor (i+1). 108 | let mut buckets: Vec<_> = (0..buckets_count) 109 | .map(|_| EdwardsPoint::identity()) 110 | .collect(); 111 | 112 | let mut columns = (0..digits_count).rev().map(|digit_index| { 113 | // Clear the buckets when processing another digit. 114 | for bucket in &mut buckets { 115 | *bucket = EdwardsPoint::identity(); 116 | } 117 | 118 | // Iterate over pairs of (point, scalar) 119 | // and add/sub the point to the corresponding bucket. 120 | // Note: if we add support for precomputed lookup tables, 121 | // we'll be adding/subtracting point premultiplied by `digits[i]` to buckets[0]. 122 | for (digits, pt) in scalars_points.iter() { 123 | // Widen digit so that we don't run into edge cases when w=8. 124 | let digit = digits[digit_index] as i16; 125 | match digit.cmp(&0) { 126 | Ordering::Greater => { 127 | let b = (digit - 1) as usize; 128 | buckets[b] = (&buckets[b] + pt).as_extended(); 129 | } 130 | Ordering::Less => { 131 | let b = (-digit - 1) as usize; 132 | buckets[b] = (&buckets[b] - pt).as_extended(); 133 | } 134 | Ordering::Equal => {} 135 | } 136 | } 137 | 138 | // Add the buckets applying the multiplication factor to each bucket. 139 | // The most efficient way to do that is to have a single sum with two running sums: 140 | // an intermediate sum from last bucket to the first, and a sum of intermediate sums. 141 | // 142 | // For example, to add buckets 1*A, 2*B, 3*C we need to add these points: 143 | // C 144 | // C B 145 | // C B A Sum = C + (C+B) + (C+B+A) 146 | let mut buckets_intermediate_sum = buckets[buckets_count - 1]; 147 | let mut buckets_sum = buckets[buckets_count - 1]; 148 | for i in (0..(buckets_count - 1)).rev() { 149 | buckets_intermediate_sum += buckets[i]; 150 | buckets_sum += buckets_intermediate_sum; 151 | } 152 | 153 | buckets_sum 154 | }); 155 | 156 | // Take the high column as an initial value to avoid wasting time doubling the identity element in `fold()`. 157 | let hi_column = columns.next().expect("should have more than zero digits"); 158 | 159 | Some(columns.fold(hi_column, |total, p| total.mul_by_pow_2(w as u32) + p)) 160 | } 161 | } 162 | 163 | #[cfg(test)] 164 | mod test { 165 | use super::*; 166 | use crate::constants; 167 | 168 | #[test] 169 | fn test_vartime_pippenger() { 170 | // Reuse points across different tests 171 | let mut n = 512; 172 | let x = Scalar::from(2128506u64).invert(); 173 | let y = Scalar::from(4443282u64).invert(); 174 | let points: Vec<_> = (0..n) 175 | .map(|i| constants::ED25519_BASEPOINT_POINT * Scalar::from(1 + i as u64)) 176 | .collect(); 177 | let scalars: Vec<_> = (0..n) 178 | .map(|i| x + (Scalar::from(i as u64) * y)) // fast way to make ~random but deterministic scalars 179 | .collect(); 180 | 181 | let premultiplied: Vec = scalars 182 | .iter() 183 | .zip(points.iter()) 184 | .map(|(sc, pt)| sc * pt) 185 | .collect(); 186 | 187 | while n > 0 { 188 | let scalars = &scalars[0..n].to_vec(); 189 | let points = &points[0..n].to_vec(); 190 | let control: EdwardsPoint = premultiplied[0..n].iter().sum(); 191 | 192 | let subject = Pippenger::vartime_multiscalar_mul(scalars.clone(), points.clone()); 193 | 194 | assert_eq!(subject.compress(), control.compress()); 195 | 196 | n /= 2; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/scalar_mul/precomputed_straus.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2019 Henry de Valence. 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - Henry de Valence 9 | 10 | //! Precomputation for Straus's method. 11 | 12 | #![allow(non_snake_case)] 13 | 14 | use alloc::vec::Vec; 15 | 16 | use core::borrow::Borrow; 17 | use core::cmp::Ordering; 18 | 19 | use crate::backend::serial::curve_models::{ 20 | AffineNielsPoint, CompletedPoint, ProjectiveNielsPoint, ProjectivePoint, 21 | }; 22 | use crate::edwards::EdwardsPoint; 23 | use crate::scalar::Scalar; 24 | use crate::traits::Identity; 25 | use crate::traits::VartimePrecomputedMultiscalarMul; 26 | use crate::window::{NafLookupTable5, NafLookupTable8}; 27 | 28 | #[allow(missing_docs)] 29 | pub struct VartimePrecomputedStraus { 30 | static_lookup_tables: Vec>, 31 | } 32 | 33 | impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { 34 | type Point = EdwardsPoint; 35 | 36 | fn new(static_points: I) -> Self 37 | where 38 | I: IntoIterator, 39 | I::Item: Borrow, 40 | { 41 | Self { 42 | static_lookup_tables: static_points 43 | .into_iter() 44 | .map(|P| NafLookupTable8::::from(P.borrow())) 45 | .collect(), 46 | } 47 | } 48 | 49 | fn len(&self) -> usize { 50 | self.static_lookup_tables.len() 51 | } 52 | 53 | fn is_empty(&self) -> bool { 54 | self.static_lookup_tables.is_empty() 55 | } 56 | 57 | fn optional_mixed_multiscalar_mul( 58 | &self, 59 | static_scalars: I, 60 | dynamic_scalars: J, 61 | dynamic_points: K, 62 | ) -> Option 63 | where 64 | I: IntoIterator, 65 | I::Item: Borrow, 66 | J: IntoIterator, 67 | J::Item: Borrow, 68 | K: IntoIterator>, 69 | { 70 | let static_nafs = static_scalars 71 | .into_iter() 72 | .map(|c| c.borrow().non_adjacent_form(5)) 73 | .collect::>(); 74 | let dynamic_nafs: Vec<_> = dynamic_scalars 75 | .into_iter() 76 | .map(|c| c.borrow().non_adjacent_form(5)) 77 | .collect::>(); 78 | 79 | let dynamic_lookup_tables = dynamic_points 80 | .into_iter() 81 | .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) 82 | .collect::>>()?; 83 | 84 | let sp = self.static_lookup_tables.len(); 85 | let dp = dynamic_lookup_tables.len(); 86 | assert!(sp >= static_nafs.len()); 87 | assert_eq!(dp, dynamic_nafs.len()); 88 | 89 | // We could save some doublings by looking for the highest 90 | // nonzero NAF coefficient, but since we might have a lot of 91 | // them to search, it's not clear it's worthwhile to check. 92 | let mut S = ProjectivePoint::identity(); 93 | for j in (0..256).rev() { 94 | let mut R: CompletedPoint = S.double(); 95 | 96 | for i in 0..dp { 97 | let t_ij = dynamic_nafs[i][j]; 98 | match t_ij.cmp(&0) { 99 | Ordering::Greater => { 100 | R = &R.as_extended() + &dynamic_lookup_tables[i].select(t_ij as usize) 101 | } 102 | Ordering::Less => { 103 | R = &R.as_extended() - &dynamic_lookup_tables[i].select(-t_ij as usize) 104 | } 105 | Ordering::Equal => {} 106 | } 107 | } 108 | 109 | #[allow(clippy::needless_range_loop)] 110 | for i in 0..static_nafs.len() { 111 | let t_ij = static_nafs[i][j]; 112 | match t_ij.cmp(&0) { 113 | Ordering::Greater => { 114 | R = &R.as_extended() + &self.static_lookup_tables[i].select(t_ij as usize) 115 | } 116 | Ordering::Less => { 117 | R = &R.as_extended() - &self.static_lookup_tables[i].select(-t_ij as usize) 118 | } 119 | Ordering::Equal => {} 120 | } 121 | } 122 | 123 | S = R.as_projective(); 124 | } 125 | 126 | Some(S.as_extended()) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/scalar_mul/straus.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | //! Implementation of the interleaved window method, also known as Straus' method. 13 | 14 | #![allow(non_snake_case)] 15 | 16 | use alloc::vec::Vec; 17 | 18 | use core::borrow::Borrow; 19 | use core::cmp::Ordering; 20 | 21 | use crate::edwards::EdwardsPoint; 22 | use crate::scalar::Scalar; 23 | use crate::traits::MultiscalarMul; 24 | use crate::traits::VartimeMultiscalarMul; 25 | 26 | /// Perform multiscalar multiplication by the interleaved window 27 | /// method, also known as Straus' method (since it was apparently 28 | /// [first published][solution] by Straus in 1964, as a solution to [a 29 | /// problem][problem] posted in the American Mathematical Monthly in 30 | /// 1963). 31 | /// 32 | /// It is easy enough to reinvent, and has been repeatedly. The basic 33 | /// idea is that when computing 34 | /// \\[ 35 | /// Q = s_1 P_1 + \cdots + s_n P_n 36 | /// \\] 37 | /// by means of additions and doublings, the doublings can be shared 38 | /// across the \\( P_i \\\). 39 | /// 40 | /// We implement two versions, a constant-time algorithm using fixed 41 | /// windows and a variable-time algorithm using sliding windows. They 42 | /// are slight variations on the same idea, and are described in more 43 | /// detail in the respective implementations. 44 | /// 45 | /// [solution]: https://www.jstor.org/stable/2310929 46 | /// [problem]: https://www.jstor.org/stable/2312273 47 | pub struct Straus {} 48 | 49 | impl MultiscalarMul for Straus { 50 | type Point = EdwardsPoint; 51 | 52 | /// Constant-time Straus using a fixed window of size \\(4\\). 53 | /// 54 | /// Our goal is to compute 55 | /// \\[ 56 | /// Q = s_1 P_1 + \cdots + s_n P_n. 57 | /// \\] 58 | /// 59 | /// For each point \\( P_i \\), precompute a lookup table of 60 | /// \\[ 61 | /// P_i, 2P_i, 3P_i, 4P_i, 5P_i, 6P_i, 7P_i, 8P_i. 62 | /// \\] 63 | /// 64 | /// For each scalar \\( s_i \\), compute its radix-\\(2^4\\) 65 | /// signed digits \\( s_{i,j} \\), i.e., 66 | /// \\[ 67 | /// s_i = s_{i,0} + s_{i,1} 16^1 + ... + s_{i,63} 16^{63}, 68 | /// \\] 69 | /// with \\( -8 \leq s_{i,j} < 8 \\). Since \\( 0 \leq |s_{i,j}| 70 | /// \leq 8 \\), we can retrieve \\( s_{i,j} P_i \\) from the 71 | /// lookup table with a conditional negation: using signed 72 | /// digits halves the required table size. 73 | /// 74 | /// Then as in the single-base fixed window case, we have 75 | /// \\[ 76 | /// \begin{aligned} 77 | /// s_i P_i &= P_i (s_{i,0} + s_{i,1} 16^1 + \cdots + s_{i,63} 16^{63}) \\\\ 78 | /// s_i P_i &= P_i s_{i,0} + P_i s_{i,1} 16^1 + \cdots + P_i s_{i,63} 16^{63} \\\\ 79 | /// s_i P_i &= P_i s_{i,0} + 16(P_i s_{i,1} + 16( \cdots +16P_i s_{i,63})\cdots ) 80 | /// \end{aligned} 81 | /// \\] 82 | /// so each \\( s_i P_i \\) can be computed by alternately adding 83 | /// a precomputed multiple \\( P_i s_{i,j} \\) of \\( P_i \\) and 84 | /// repeatedly doubling. 85 | /// 86 | /// Now consider the two-dimensional sum 87 | /// \\[ 88 | /// \begin{aligned} 89 | /// s\_1 P\_1 &=& P\_1 s\_{1,0} &+& 16 (P\_1 s\_{1,1} &+& 16 ( \cdots &+& 16 P\_1 s\_{1,63}&) \cdots ) \\\\ 90 | /// + & & + & & + & & & & + & \\\\ 91 | /// s\_2 P\_2 &=& P\_2 s\_{2,0} &+& 16 (P\_2 s\_{2,1} &+& 16 ( \cdots &+& 16 P\_2 s\_{2,63}&) \cdots ) \\\\ 92 | /// + & & + & & + & & & & + & \\\\ 93 | /// \vdots & & \vdots & & \vdots & & & & \vdots & \\\\ 94 | /// + & & + & & + & & & & + & \\\\ 95 | /// s\_n P\_n &=& P\_n s\_{n,0} &+& 16 (P\_n s\_{n,1} &+& 16 ( \cdots &+& 16 P\_n s\_{n,63}&) \cdots ) 96 | /// \end{aligned} 97 | /// \\] 98 | /// The sum of the left-hand column is the result \\( Q \\); by 99 | /// computing the two-dimensional sum on the right column-wise, 100 | /// top-to-bottom, then right-to-left, we need to multiply by \\( 101 | /// 16\\) only once per column, sharing the doublings across all 102 | /// of the input points. 103 | fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint 104 | where 105 | I: IntoIterator, 106 | I::Item: Borrow, 107 | J: IntoIterator, 108 | J::Item: Borrow, 109 | { 110 | use crate::backend::serial::curve_models::ProjectiveNielsPoint; 111 | use crate::traits::Identity; 112 | use crate::window::LookupTable; 113 | 114 | let lookup_tables: Vec<_> = points 115 | .into_iter() 116 | .map(|point| LookupTable::::from(point.borrow())) 117 | .collect(); 118 | 119 | // This puts the scalar digits into a heap-allocated Vec. 120 | // To ensure that these are erased, pass ownership of the Vec into a 121 | // Zeroizing wrapper. 122 | #[cfg_attr(not(feature = "zeroize"), allow(unused_mut))] 123 | let mut scalar_digits: Vec<_> = scalars 124 | .into_iter() 125 | .map(|s| s.borrow().as_radix_16()) 126 | .collect(); 127 | 128 | let mut Q = EdwardsPoint::identity(); 129 | for j in (0..64).rev() { 130 | Q = Q.mul_by_pow_2(4); 131 | let it = scalar_digits.iter().zip(lookup_tables.iter()); 132 | for (s_i, lookup_table_i) in it { 133 | // R_i = s_{i,j} * P_i 134 | let R_i = lookup_table_i.select(s_i[j]); 135 | // Q = Q + R_i 136 | Q = (&Q + &R_i).as_extended(); 137 | } 138 | } 139 | 140 | #[cfg(feature = "zeroize")] 141 | zeroize::Zeroize::zeroize(&mut scalar_digits); 142 | 143 | Q 144 | } 145 | } 146 | 147 | impl VartimeMultiscalarMul for Straus { 148 | type Point = EdwardsPoint; 149 | 150 | /// Variable-time Straus using a non-adjacent form of width \\(5\\). 151 | /// 152 | /// This is completely similar to the constant-time code, but we 153 | /// use a non-adjacent form for the scalar, and do not do table 154 | /// lookups in constant time. 155 | /// 156 | /// The non-adjacent form has signed, odd digits. Using only odd 157 | /// digits halves the table size (since we only need odd 158 | /// multiples), or gives fewer additions for the same table size. 159 | fn optional_multiscalar_mul(scalars: I, points: J) -> Option 160 | where 161 | I: IntoIterator, 162 | I::Item: Borrow, 163 | J: IntoIterator>, 164 | { 165 | use crate::backend::serial::curve_models::{ 166 | CompletedPoint, ProjectiveNielsPoint, ProjectivePoint, 167 | }; 168 | use crate::traits::Identity; 169 | use crate::window::NafLookupTable5; 170 | 171 | let nafs: Vec<_> = scalars 172 | .into_iter() 173 | .map(|c| c.borrow().non_adjacent_form(5)) 174 | .collect(); 175 | 176 | let lookup_tables = points 177 | .into_iter() 178 | .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) 179 | .collect::>>()?; 180 | 181 | let mut r = ProjectivePoint::identity(); 182 | 183 | for i in (0..256).rev() { 184 | let mut t: CompletedPoint = r.double(); 185 | 186 | for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { 187 | match naf[i].cmp(&0) { 188 | Ordering::Greater => { 189 | t = &t.as_extended() + &lookup_table.select(naf[i] as usize) 190 | } 191 | Ordering::Less => t = &t.as_extended() - &lookup_table.select(-naf[i] as usize), 192 | Ordering::Equal => {} 193 | } 194 | } 195 | 196 | r = t.as_projective(); 197 | } 198 | 199 | Some(r.as_extended()) 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/scalar_mul/variable_base.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | use crate::backend::serial::curve_models::ProjectiveNielsPoint; 4 | use crate::edwards::EdwardsPoint; 5 | use crate::scalar::Scalar; 6 | use crate::traits::Identity; 7 | use crate::window::LookupTable; 8 | 9 | /// Perform constant-time, variable-base scalar multiplication. 10 | #[rustfmt::skip] // keep alignment of explanatory comments 11 | pub(crate) fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { 12 | // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] 13 | let lookup_table = LookupTable::::from(point); 14 | // Setting s = scalar, compute 15 | // 16 | // s = s_0 + s_1*16^1 + ... + s_63*16^63, 17 | // 18 | // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`. 19 | // This decomposition requires s < 2^255, which is guaranteed by Scalar invariant #1. 20 | let scalar_digits = scalar.as_radix_16(); 21 | // Compute s*P as 22 | // 23 | // s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63) 24 | // s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63 25 | // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...)) 26 | // 27 | // We sum right-to-left. 28 | 29 | // Unwrap first loop iteration to save computing 16*identity 30 | let mut tmp2; 31 | let mut tmp3 = EdwardsPoint::identity(); 32 | let mut tmp1 = &tmp3 + &lookup_table.select(scalar_digits[63]); 33 | // Now tmp1 = s_63*P in P1xP1 coords 34 | for i in (0..63).rev() { 35 | tmp2 = tmp1.as_projective(); // tmp2 = (prev) in P2 coords 36 | tmp1 = tmp2.double(); // tmp1 = 2*(prev) in P1xP1 coords 37 | tmp2 = tmp1.as_projective(); // tmp2 = 2*(prev) in P2 coords 38 | tmp1 = tmp2.double(); // tmp1 = 4*(prev) in P1xP1 coords 39 | tmp2 = tmp1.as_projective(); // tmp2 = 4*(prev) in P2 coords 40 | tmp1 = tmp2.double(); // tmp1 = 8*(prev) in P1xP1 coords 41 | tmp2 = tmp1.as_projective(); // tmp2 = 8*(prev) in P2 coords 42 | tmp1 = tmp2.double(); // tmp1 = 16*(prev) in P1xP1 coords 43 | tmp3 = tmp1.as_extended(); // tmp3 = 16*(prev) in P3 coords 44 | tmp1 = &tmp3 + &lookup_table.select(scalar_digits[i]); 45 | // Now tmp1 = s_i*P + 16*(prev) in P1xP1 coords 46 | } 47 | tmp1.as_extended() 48 | } 49 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/scalar_mul/vartime_double_base.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | #![allow(non_snake_case)] 12 | 13 | use core::cmp::Ordering; 14 | 15 | use crate::backend::serial::curve_models::{ProjectiveNielsPoint, ProjectivePoint}; 16 | use crate::constants; 17 | use crate::edwards::EdwardsPoint; 18 | use crate::scalar::Scalar; 19 | use crate::traits::Identity; 20 | use crate::window::NafLookupTable5; 21 | 22 | /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. 23 | pub fn mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { 24 | let a_naf = a.non_adjacent_form(5); 25 | 26 | #[cfg(feature = "precomputed-tables")] 27 | let b_naf = b.non_adjacent_form(8); 28 | #[cfg(not(feature = "precomputed-tables"))] 29 | let b_naf = b.non_adjacent_form(5); 30 | 31 | // Find starting index 32 | let mut i: usize = 255; 33 | for j in (0..256).rev() { 34 | i = j; 35 | if a_naf[i] != 0 || b_naf[i] != 0 { 36 | break; 37 | } 38 | } 39 | 40 | let table_A = NafLookupTable5::::from(A); 41 | #[cfg(feature = "precomputed-tables")] 42 | let table_B = &constants::AFFINE_ODD_MULTIPLES_OF_BASEPOINT; 43 | #[cfg(not(feature = "precomputed-tables"))] 44 | let table_B = 45 | &NafLookupTable5::::from(&constants::ED25519_BASEPOINT_POINT); 46 | 47 | let mut r = ProjectivePoint::identity(); 48 | loop { 49 | let mut t = r.double(); 50 | 51 | match a_naf[i].cmp(&0) { 52 | Ordering::Greater => t = &t.as_extended() + &table_A.select(a_naf[i] as usize), 53 | Ordering::Less => t = &t.as_extended() - &table_A.select(-a_naf[i] as usize), 54 | Ordering::Equal => {} 55 | } 56 | 57 | match b_naf[i].cmp(&0) { 58 | Ordering::Greater => t = &t.as_extended() + &table_B.select(b_naf[i] as usize), 59 | Ordering::Less => t = &t.as_extended() - &table_B.select(-b_naf[i] as usize), 60 | Ordering::Equal => {} 61 | } 62 | 63 | r = t.as_projective(); 64 | 65 | if i == 0 { 66 | break; 67 | } 68 | i -= 1; 69 | } 70 | 71 | r.as_extended() 72 | } 73 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/u32/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | //! The `u32` backend uses `u32`s and a `(u32, u32) -> u64` multiplier. 13 | //! 14 | //! This code is intended to be portable, but it requires that 15 | //! multiplication of two \\(32\\)-bit values to a \\(64\\)-bit result 16 | //! is constant-time on the target platform. 17 | 18 | pub mod field; 19 | 20 | pub mod scalar; 21 | 22 | pub mod constants; 23 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/serial/u64/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2018 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | //! The `u64` backend uses `u64`s and a `(u64, u64) -> u128` multiplier. 13 | //! 14 | //! On x86_64, the idiom `(x as u128) * (y as u128)` lowers to `MUL` 15 | //! instructions taking 64-bit inputs and producing 128-bit outputs. On 16 | //! other platforms, this implementation is not recommended. 17 | //! 18 | //! On Haswell and newer, the BMI2 extension provides `MULX`, and on 19 | //! Broadwell and newer, the ADX extension provides `ADCX` and `ADOX` 20 | //! (allowing the CPU to compute two carry chains in parallel). These 21 | //! will be used if available. 22 | 23 | pub mod field; 24 | 25 | pub mod scalar; 26 | 27 | pub mod constants; 28 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/vector/avx2/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | #![doc = include_str!("../../../../docs/avx2-notes.md")] 13 | 14 | pub(crate) mod field; 15 | 16 | pub(crate) mod edwards; 17 | 18 | pub(crate) mod constants; 19 | 20 | pub(crate) use self::edwards::{CachedPoint, ExtendedPoint}; 21 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/vector/ifma/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2018-2019 Henry de Valence 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - Henry de Valence 9 | 10 | #![doc = include_str!("../../../../docs/ifma-notes.md")] 11 | 12 | #[allow(missing_docs)] 13 | pub mod field; 14 | 15 | #[allow(missing_docs)] 16 | pub mod edwards; 17 | 18 | pub mod constants; 19 | 20 | pub(crate) use self::edwards::{CachedPoint, ExtendedPoint}; 21 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/vector/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | #![doc = include_str!("../../../docs/parallel-formulas.md")] 13 | 14 | #[allow(missing_docs)] 15 | pub mod packed_simd; 16 | 17 | pub mod avx2; 18 | 19 | #[cfg(all(curve25519_dalek_backend = "unstable_avx512", nightly))] 20 | pub mod ifma; 21 | 22 | pub mod scalar_mul; 23 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/vector/scalar_mul/mod.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | //! Implementations of various multiplication algorithms for the SIMD backends. 13 | 14 | #[allow(missing_docs)] 15 | pub mod variable_base; 16 | 17 | #[allow(missing_docs)] 18 | pub mod vartime_double_base; 19 | 20 | #[allow(missing_docs)] 21 | #[cfg(feature = "alloc")] 22 | pub mod straus; 23 | 24 | #[allow(missing_docs)] 25 | #[cfg(feature = "alloc")] 26 | pub mod precomputed_straus; 27 | 28 | #[allow(missing_docs)] 29 | #[cfg(feature = "alloc")] 30 | pub mod pippenger; 31 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/vector/scalar_mul/pippenger.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2019 Oleg Andreev 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - Oleg Andreev 9 | 10 | #![allow(non_snake_case)] 11 | 12 | #[curve25519_dalek_derive::unsafe_target_feature_specialize( 13 | "avx2", 14 | conditional( 15 | "avx512ifma,avx512vl", 16 | all(curve25519_dalek_backend = "unstable_avx512", nightly) 17 | ) 18 | )] 19 | pub mod spec { 20 | 21 | use alloc::vec::Vec; 22 | 23 | use core::borrow::Borrow; 24 | use core::cmp::Ordering; 25 | 26 | #[for_target_feature("avx2")] 27 | use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; 28 | 29 | #[for_target_feature("avx512ifma")] 30 | use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; 31 | 32 | use crate::edwards::EdwardsPoint; 33 | use crate::scalar::Scalar; 34 | use crate::traits::{Identity, VartimeMultiscalarMul}; 35 | 36 | /// Implements a version of Pippenger's algorithm. 37 | /// 38 | /// See the documentation in the serial `scalar_mul::pippenger` module for details. 39 | pub struct Pippenger; 40 | 41 | impl VartimeMultiscalarMul for Pippenger { 42 | type Point = EdwardsPoint; 43 | 44 | fn optional_multiscalar_mul(scalars: I, points: J) -> Option 45 | where 46 | I: IntoIterator, 47 | I::Item: Borrow, 48 | J: IntoIterator>, 49 | { 50 | let mut scalars = scalars.into_iter(); 51 | let size = scalars.by_ref().size_hint().0; 52 | let w = if size < 500 { 53 | 6 54 | } else if size < 800 { 55 | 7 56 | } else { 57 | 8 58 | }; 59 | 60 | let max_digit: usize = 1 << w; 61 | let digits_count: usize = Scalar::to_radix_2w_size_hint(w); 62 | let buckets_count: usize = max_digit / 2; // digits are signed+centered hence 2^w/2, excluding 0-th bucket 63 | 64 | // Collect optimized scalars and points in a buffer for repeated access 65 | // (scanning the whole collection per each digit position). 66 | let scalars = scalars.map(|s| s.borrow().as_radix_2w(w)); 67 | 68 | let points = points 69 | .into_iter() 70 | .map(|p| p.map(|P| CachedPoint::from(ExtendedPoint::from(P)))); 71 | 72 | let scalars_points = scalars 73 | .zip(points) 74 | .map(|(s, maybe_p)| maybe_p.map(|p| (s, p))) 75 | .collect::>>()?; 76 | 77 | // Prepare 2^w/2 buckets. 78 | // buckets[i] corresponds to a multiplication factor (i+1). 79 | let mut buckets: Vec = (0..buckets_count) 80 | .map(|_| ExtendedPoint::identity()) 81 | .collect(); 82 | 83 | let mut columns = (0..digits_count).rev().map(|digit_index| { 84 | // Clear the buckets when processing another digit. 85 | for bucket in &mut buckets { 86 | *bucket = ExtendedPoint::identity(); 87 | } 88 | 89 | // Iterate over pairs of (point, scalar) 90 | // and add/sub the point to the corresponding bucket. 91 | // Note: if we add support for precomputed lookup tables, 92 | // we'll be adding/subtractiong point premultiplied by `digits[i]` to buckets[0]. 93 | for (digits, pt) in scalars_points.iter() { 94 | // Widen digit so that we don't run into edge cases when w=8. 95 | let digit = digits[digit_index] as i16; 96 | match digit.cmp(&0) { 97 | Ordering::Greater => { 98 | let b = (digit - 1) as usize; 99 | buckets[b] = &buckets[b] + pt; 100 | } 101 | Ordering::Less => { 102 | let b = (-digit - 1) as usize; 103 | buckets[b] = &buckets[b] - pt; 104 | } 105 | Ordering::Equal => {} 106 | } 107 | } 108 | 109 | // Add the buckets applying the multiplication factor to each bucket. 110 | // The most efficient way to do that is to have a single sum with two running sums: 111 | // an intermediate sum from last bucket to the first, and a sum of intermediate sums. 112 | // 113 | // For example, to add buckets 1*A, 2*B, 3*C we need to add these points: 114 | // C 115 | // C B 116 | // C B A Sum = C + (C+B) + (C+B+A) 117 | let mut buckets_intermediate_sum = buckets[buckets_count - 1]; 118 | let mut buckets_sum = buckets[buckets_count - 1]; 119 | for i in (0..(buckets_count - 1)).rev() { 120 | buckets_intermediate_sum = 121 | &buckets_intermediate_sum + &CachedPoint::from(buckets[i]); 122 | buckets_sum = &buckets_sum + &CachedPoint::from(buckets_intermediate_sum); 123 | } 124 | 125 | buckets_sum 126 | }); 127 | 128 | // Take the high column as an initial value to avoid wasting time doubling the identity element in `fold()`. 129 | let hi_column = columns.next().expect("should have more than zero digits"); 130 | 131 | Some( 132 | columns 133 | .fold(hi_column, |total, p| { 134 | &total.mul_by_pow_2(w as u32) + &CachedPoint::from(p) 135 | }) 136 | .into(), 137 | ) 138 | } 139 | } 140 | 141 | #[cfg(test)] 142 | mod test { 143 | #[test] 144 | fn test_vartime_pippenger() { 145 | use super::*; 146 | use crate::constants; 147 | use crate::scalar::Scalar; 148 | 149 | // Reuse points across different tests 150 | let mut n = 512; 151 | let x = Scalar::from(2128506u64).invert(); 152 | let y = Scalar::from(4443282u64).invert(); 153 | let points: Vec<_> = (0..n) 154 | .map(|i| constants::ED25519_BASEPOINT_POINT * Scalar::from(1 + i as u64)) 155 | .collect(); 156 | let scalars: Vec<_> = (0..n) 157 | .map(|i| x + (Scalar::from(i as u64) * y)) // fast way to make ~random but deterministic scalars 158 | .collect(); 159 | 160 | let premultiplied: Vec = scalars 161 | .iter() 162 | .zip(points.iter()) 163 | .map(|(sc, pt)| sc * pt) 164 | .collect(); 165 | 166 | while n > 0 { 167 | let scalars = &scalars[0..n].to_vec(); 168 | let points = &points[0..n].to_vec(); 169 | let control: EdwardsPoint = premultiplied[0..n].iter().sum(); 170 | 171 | let subject = Pippenger::vartime_multiscalar_mul(scalars.clone(), points.clone()); 172 | 173 | assert_eq!(subject.compress(), control.compress()); 174 | 175 | n = n / 2; 176 | } 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/vector/scalar_mul/precomputed_straus.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2019 Henry de Valence. 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - Henry de Valence 9 | 10 | //! Precomputation for Straus's method. 11 | 12 | #![allow(non_snake_case)] 13 | 14 | #[curve25519_dalek_derive::unsafe_target_feature_specialize( 15 | "avx2", 16 | conditional( 17 | "avx512ifma,avx512vl", 18 | all(curve25519_dalek_backend = "unstable_avx512", nightly) 19 | ) 20 | )] 21 | pub mod spec { 22 | 23 | use alloc::vec::Vec; 24 | 25 | use core::borrow::Borrow; 26 | use core::cmp::Ordering; 27 | 28 | #[for_target_feature("avx2")] 29 | use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; 30 | 31 | #[for_target_feature("avx512ifma")] 32 | use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; 33 | 34 | use crate::edwards::EdwardsPoint; 35 | use crate::scalar::Scalar; 36 | use crate::traits::Identity; 37 | use crate::traits::VartimePrecomputedMultiscalarMul; 38 | use crate::window::{NafLookupTable5, NafLookupTable8}; 39 | 40 | pub struct VartimePrecomputedStraus { 41 | static_lookup_tables: Vec>, 42 | } 43 | 44 | impl VartimePrecomputedMultiscalarMul for VartimePrecomputedStraus { 45 | type Point = EdwardsPoint; 46 | 47 | fn new(static_points: I) -> Self 48 | where 49 | I: IntoIterator, 50 | I::Item: Borrow, 51 | { 52 | Self { 53 | static_lookup_tables: static_points 54 | .into_iter() 55 | .map(|P| NafLookupTable8::::from(P.borrow())) 56 | .collect(), 57 | } 58 | } 59 | 60 | fn len(&self) -> usize { 61 | self.static_lookup_tables.len() 62 | } 63 | 64 | fn is_empty(&self) -> bool { 65 | self.static_lookup_tables.is_empty() 66 | } 67 | 68 | fn optional_mixed_multiscalar_mul( 69 | &self, 70 | static_scalars: I, 71 | dynamic_scalars: J, 72 | dynamic_points: K, 73 | ) -> Option 74 | where 75 | I: IntoIterator, 76 | I::Item: Borrow, 77 | J: IntoIterator, 78 | J::Item: Borrow, 79 | K: IntoIterator>, 80 | { 81 | let static_nafs = static_scalars 82 | .into_iter() 83 | .map(|c| c.borrow().non_adjacent_form(5)) 84 | .collect::>(); 85 | let dynamic_nafs: Vec<_> = dynamic_scalars 86 | .into_iter() 87 | .map(|c| c.borrow().non_adjacent_form(5)) 88 | .collect::>(); 89 | 90 | let dynamic_lookup_tables = dynamic_points 91 | .into_iter() 92 | .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) 93 | .collect::>>()?; 94 | 95 | let sp = self.static_lookup_tables.len(); 96 | let dp = dynamic_lookup_tables.len(); 97 | assert!(sp >= static_nafs.len()); 98 | assert_eq!(dp, dynamic_nafs.len()); 99 | 100 | // We could save some doublings by looking for the highest 101 | // nonzero NAF coefficient, but since we might have a lot of 102 | // them to search, it's not clear it's worthwhile to check. 103 | let mut R = ExtendedPoint::identity(); 104 | for j in (0..256).rev() { 105 | R = R.double(); 106 | 107 | for i in 0..dp { 108 | let t_ij = dynamic_nafs[i][j]; 109 | match t_ij.cmp(&0) { 110 | Ordering::Greater => { 111 | R = &R + &dynamic_lookup_tables[i].select(t_ij as usize); 112 | } 113 | Ordering::Less => { 114 | R = &R - &dynamic_lookup_tables[i].select(-t_ij as usize); 115 | } 116 | Ordering::Equal => {} 117 | } 118 | } 119 | 120 | #[allow(clippy::needless_range_loop)] 121 | for i in 0..static_nafs.len() { 122 | let t_ij = static_nafs[i][j]; 123 | match t_ij.cmp(&0) { 124 | Ordering::Greater => { 125 | R = &R + &self.static_lookup_tables[i].select(t_ij as usize); 126 | } 127 | Ordering::Less => { 128 | R = &R - &self.static_lookup_tables[i].select(-t_ij as usize); 129 | } 130 | Ordering::Equal => {} 131 | } 132 | } 133 | } 134 | 135 | Some(R.into()) 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/vector/scalar_mul/straus.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | #![allow(non_snake_case)] 13 | 14 | #[curve25519_dalek_derive::unsafe_target_feature_specialize( 15 | "avx2", 16 | conditional( 17 | "avx512ifma,avx512vl", 18 | all(curve25519_dalek_backend = "unstable_avx512", nightly) 19 | ) 20 | )] 21 | pub mod spec { 22 | 23 | use alloc::vec::Vec; 24 | 25 | use core::borrow::Borrow; 26 | use core::cmp::Ordering; 27 | 28 | #[cfg(feature = "zeroize")] 29 | use zeroize::Zeroizing; 30 | 31 | #[for_target_feature("avx2")] 32 | use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; 33 | 34 | #[for_target_feature("avx512ifma")] 35 | use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; 36 | 37 | use crate::edwards::EdwardsPoint; 38 | use crate::scalar::Scalar; 39 | use crate::traits::{Identity, MultiscalarMul, VartimeMultiscalarMul}; 40 | use crate::window::{LookupTable, NafLookupTable5}; 41 | 42 | /// Multiscalar multiplication using interleaved window / Straus' 43 | /// method. See the `Straus` struct in the serial backend for more 44 | /// details. 45 | /// 46 | /// This exists as a separate implementation from that one because the 47 | /// AVX2 code uses different curve models (it does not pass between 48 | /// multiple models during scalar mul), and it has to convert the 49 | /// point representation on the fly. 50 | pub struct Straus {} 51 | 52 | impl MultiscalarMul for Straus { 53 | type Point = EdwardsPoint; 54 | 55 | fn multiscalar_mul(scalars: I, points: J) -> EdwardsPoint 56 | where 57 | I: IntoIterator, 58 | I::Item: Borrow, 59 | J: IntoIterator, 60 | J::Item: Borrow, 61 | { 62 | // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] 63 | // for each input point P 64 | let lookup_tables: Vec<_> = points 65 | .into_iter() 66 | .map(|point| LookupTable::::from(point.borrow())) 67 | .collect(); 68 | 69 | let scalar_digits_vec: Vec<_> = scalars 70 | .into_iter() 71 | .map(|s| s.borrow().as_radix_16()) 72 | .collect(); 73 | // Pass ownership to a `Zeroizing` wrapper 74 | #[cfg(feature = "zeroize")] 75 | let scalar_digits_vec = Zeroizing::new(scalar_digits_vec); 76 | 77 | let mut Q = ExtendedPoint::identity(); 78 | for j in (0..64).rev() { 79 | Q = Q.mul_by_pow_2(4); 80 | let it = scalar_digits_vec.iter().zip(lookup_tables.iter()); 81 | for (s_i, lookup_table_i) in it { 82 | // Q = Q + s_{i,j} * P_i 83 | Q = &Q + &lookup_table_i.select(s_i[j]); 84 | } 85 | } 86 | Q.into() 87 | } 88 | } 89 | 90 | impl VartimeMultiscalarMul for Straus { 91 | type Point = EdwardsPoint; 92 | 93 | fn optional_multiscalar_mul(scalars: I, points: J) -> Option 94 | where 95 | I: IntoIterator, 96 | I::Item: Borrow, 97 | J: IntoIterator>, 98 | { 99 | let nafs: Vec<_> = scalars 100 | .into_iter() 101 | .map(|c| c.borrow().non_adjacent_form(5)) 102 | .collect(); 103 | let lookup_tables: Vec<_> = points 104 | .into_iter() 105 | .map(|P_opt| P_opt.map(|P| NafLookupTable5::::from(&P))) 106 | .collect::>>()?; 107 | 108 | let mut Q = ExtendedPoint::identity(); 109 | 110 | for i in (0..256).rev() { 111 | Q = Q.double(); 112 | 113 | for (naf, lookup_table) in nafs.iter().zip(lookup_tables.iter()) { 114 | match naf[i].cmp(&0) { 115 | Ordering::Greater => { 116 | Q = &Q + &lookup_table.select(naf[i] as usize); 117 | } 118 | Ordering::Less => { 119 | Q = &Q - &lookup_table.select(-naf[i] as usize); 120 | } 121 | Ordering::Equal => {} 122 | } 123 | } 124 | } 125 | 126 | Some(Q.into()) 127 | } 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/vector/scalar_mul/variable_base.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_snake_case)] 2 | 3 | #[curve25519_dalek_derive::unsafe_target_feature_specialize( 4 | "avx2", 5 | conditional( 6 | "avx512ifma,avx512vl", 7 | all(curve25519_dalek_backend = "unstable_avx512", nightly) 8 | ) 9 | )] 10 | pub mod spec { 11 | 12 | #[for_target_feature("avx2")] 13 | use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; 14 | 15 | #[for_target_feature("avx512ifma")] 16 | use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; 17 | 18 | use crate::edwards::EdwardsPoint; 19 | use crate::scalar::Scalar; 20 | use crate::traits::Identity; 21 | use crate::window::LookupTable; 22 | 23 | /// Perform constant-time, variable-base scalar multiplication. 24 | pub fn mul(point: &EdwardsPoint, scalar: &Scalar) -> EdwardsPoint { 25 | // Construct a lookup table of [P,2P,3P,4P,5P,6P,7P,8P] 26 | let lookup_table = LookupTable::::from(point); 27 | // Setting s = scalar, compute 28 | // 29 | // s = s_0 + s_1*16^1 + ... + s_63*16^63, 30 | // 31 | // with `-8 ≤ s_i < 8` for `0 ≤ i < 63` and `-8 ≤ s_63 ≤ 8`. 32 | let scalar_digits = scalar.as_radix_16(); 33 | // Compute s*P as 34 | // 35 | // s*P = P*(s_0 + s_1*16^1 + s_2*16^2 + ... + s_63*16^63) 36 | // s*P = P*s_0 + P*s_1*16^1 + P*s_2*16^2 + ... + P*s_63*16^63 37 | // s*P = P*s_0 + 16*(P*s_1 + 16*(P*s_2 + 16*( ... + P*s_63)...)) 38 | // 39 | // We sum right-to-left. 40 | let mut Q = ExtendedPoint::identity(); 41 | for i in (0..64).rev() { 42 | Q = Q.mul_by_pow_2(4); 43 | Q = &Q + &lookup_table.select(scalar_digits[i]); 44 | } 45 | Q.into() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /curve25519-dalek/src/backend/vector/scalar_mul/vartime_double_base.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | #![allow(non_snake_case)] 13 | 14 | #[curve25519_dalek_derive::unsafe_target_feature_specialize( 15 | "avx2", 16 | conditional( 17 | "avx512ifma,avx512vl", 18 | all(curve25519_dalek_backend = "unstable_avx512", nightly) 19 | ) 20 | )] 21 | pub mod spec { 22 | 23 | use core::cmp::Ordering; 24 | 25 | #[for_target_feature("avx2")] 26 | use crate::backend::vector::avx2::{CachedPoint, ExtendedPoint}; 27 | 28 | #[for_target_feature("avx512ifma")] 29 | use crate::backend::vector::ifma::{CachedPoint, ExtendedPoint}; 30 | 31 | #[cfg(feature = "precomputed-tables")] 32 | #[for_target_feature("avx2")] 33 | use crate::backend::vector::avx2::constants::BASEPOINT_ODD_LOOKUP_TABLE; 34 | 35 | #[cfg(feature = "precomputed-tables")] 36 | #[for_target_feature("avx512ifma")] 37 | use crate::backend::vector::ifma::constants::BASEPOINT_ODD_LOOKUP_TABLE; 38 | 39 | use crate::edwards::EdwardsPoint; 40 | use crate::scalar::Scalar; 41 | use crate::traits::Identity; 42 | use crate::window::NafLookupTable5; 43 | 44 | /// Compute \\(aA + bB\\) in variable time, where \\(B\\) is the Ed25519 basepoint. 45 | pub fn mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> EdwardsPoint { 46 | let a_naf = a.non_adjacent_form(5); 47 | 48 | #[cfg(feature = "precomputed-tables")] 49 | let b_naf = b.non_adjacent_form(8); 50 | #[cfg(not(feature = "precomputed-tables"))] 51 | let b_naf = b.non_adjacent_form(5); 52 | 53 | // Find starting index 54 | let mut i: usize = 255; 55 | for j in (0..256).rev() { 56 | i = j; 57 | if a_naf[i] != 0 || b_naf[i] != 0 { 58 | break; 59 | } 60 | } 61 | 62 | let table_A = NafLookupTable5::::from(A); 63 | 64 | #[cfg(feature = "precomputed-tables")] 65 | let table_B = &BASEPOINT_ODD_LOOKUP_TABLE; 66 | 67 | #[cfg(not(feature = "precomputed-tables"))] 68 | let table_B = 69 | &NafLookupTable5::::from(&crate::constants::ED25519_BASEPOINT_POINT); 70 | 71 | let mut Q = ExtendedPoint::identity(); 72 | 73 | loop { 74 | Q = Q.double(); 75 | 76 | match a_naf[i].cmp(&0) { 77 | Ordering::Greater => { 78 | Q = &Q + &table_A.select(a_naf[i] as usize); 79 | } 80 | Ordering::Less => { 81 | Q = &Q - &table_A.select(-a_naf[i] as usize); 82 | } 83 | Ordering::Equal => {} 84 | } 85 | 86 | match b_naf[i].cmp(&0) { 87 | Ordering::Greater => { 88 | Q = &Q + &table_B.select(b_naf[i] as usize); 89 | } 90 | Ordering::Less => { 91 | Q = &Q - &table_B.select(-b_naf[i] as usize); 92 | } 93 | Ordering::Equal => {} 94 | } 95 | 96 | if i == 0 { 97 | break; 98 | } 99 | i -= 1; 100 | } 101 | 102 | Q.into() 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /curve25519-dalek/src/constants.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | //! Various constants, such as the Ristretto and Ed25519 basepoints. 12 | 13 | #![allow(non_snake_case)] 14 | 15 | use cfg_if::cfg_if; 16 | 17 | use crate::edwards::CompressedEdwardsY; 18 | use crate::montgomery::MontgomeryPoint; 19 | use crate::ristretto::{CompressedRistretto, RistrettoPoint}; 20 | use crate::scalar::Scalar; 21 | 22 | #[cfg(feature = "precomputed-tables")] 23 | use crate::edwards::EdwardsBasepointTable; 24 | 25 | cfg_if! { 26 | if #[cfg(curve25519_dalek_backend = "fiat")] { 27 | #[cfg(curve25519_dalek_bits = "32")] 28 | pub use crate::backend::serial::fiat_u32::constants::*; 29 | #[cfg(curve25519_dalek_bits = "64")] 30 | pub use crate::backend::serial::fiat_u64::constants::*; 31 | } else { 32 | #[cfg(curve25519_dalek_bits = "32")] 33 | pub use crate::backend::serial::u32::constants::*; 34 | #[cfg(curve25519_dalek_bits = "64")] 35 | pub use crate::backend::serial::u64::constants::*; 36 | } 37 | } 38 | 39 | /// The Ed25519 basepoint, in `CompressedEdwardsY` format. 40 | /// 41 | /// This is the little-endian byte encoding of \\( 4/5 \pmod p \\), 42 | /// which is the \\(y\\)-coordinate of the Ed25519 basepoint. 43 | /// 44 | /// The sign bit is 0 since the basepoint has \\(x\\) chosen to be positive. 45 | pub const ED25519_BASEPOINT_COMPRESSED: CompressedEdwardsY = CompressedEdwardsY([ 46 | 0x58, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 47 | 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 48 | ]); 49 | 50 | /// The X25519 basepoint, in `MontgomeryPoint` format. 51 | pub const X25519_BASEPOINT: MontgomeryPoint = MontgomeryPoint([ 52 | 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 53 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 54 | ]); 55 | 56 | /// The Ristretto basepoint, in `CompressedRistretto` format. 57 | pub const RISTRETTO_BASEPOINT_COMPRESSED: CompressedRistretto = CompressedRistretto([ 58 | 0xe2, 0xf2, 0xae, 0x0a, 0x6a, 0xbc, 0x4e, 0x71, 0xa8, 0x84, 0xa9, 0x61, 0xc5, 0x00, 0x51, 0x5f, 59 | 0x58, 0xe3, 0x0b, 0x6a, 0xa5, 0x82, 0xdd, 0x8d, 0xb6, 0xa6, 0x59, 0x45, 0xe0, 0x8d, 0x2d, 0x76, 60 | ]); 61 | 62 | /// The Ristretto basepoint, as a `RistrettoPoint`. 63 | /// 64 | /// This is called `_POINT` to distinguish it from `_TABLE`, which 65 | /// provides fast scalar multiplication. 66 | pub const RISTRETTO_BASEPOINT_POINT: RistrettoPoint = RistrettoPoint(ED25519_BASEPOINT_POINT); 67 | 68 | /// `BASEPOINT_ORDER` is the order of the Ristretto group and of the Ed25519 basepoint, i.e., 69 | /// $$ 70 | /// \ell = 2^\{252\} + 27742317777372353535851937790883648493. 71 | /// $$ 72 | pub(crate) const BASEPOINT_ORDER: Scalar = Scalar { 73 | bytes: [ 74 | 0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 75 | 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 76 | 0x00, 0x10, 77 | ], 78 | }; 79 | 80 | #[cfg(feature = "precomputed-tables")] 81 | use crate::ristretto::RistrettoBasepointTable; 82 | 83 | /// The Ristretto basepoint, as a `RistrettoBasepointTable` for scalar multiplication. 84 | #[cfg(feature = "precomputed-tables")] 85 | pub static RISTRETTO_BASEPOINT_TABLE: &RistrettoBasepointTable = unsafe { 86 | // SAFETY: `RistrettoBasepointTable` is a `#[repr(transparent)]` newtype of 87 | // `EdwardsBasepointTable` 88 | &*(ED25519_BASEPOINT_TABLE as *const EdwardsBasepointTable as *const RistrettoBasepointTable) 89 | }; 90 | 91 | #[cfg(test)] 92 | mod test { 93 | use crate::constants; 94 | use crate::field::FieldElement; 95 | use crate::traits::{IsIdentity, ValidityCheck}; 96 | 97 | #[test] 98 | fn test_eight_torsion() { 99 | for i in 0..8 { 100 | let Q = constants::EIGHT_TORSION[i].mul_by_pow_2(3); 101 | assert!(Q.is_valid()); 102 | assert!(Q.is_identity()); 103 | } 104 | } 105 | 106 | #[test] 107 | fn test_four_torsion() { 108 | for i in (0..8).filter(|i| i % 2 == 0) { 109 | let Q = constants::EIGHT_TORSION[i].mul_by_pow_2(2); 110 | assert!(Q.is_valid()); 111 | assert!(Q.is_identity()); 112 | } 113 | } 114 | 115 | #[test] 116 | fn test_two_torsion() { 117 | for i in (0..8).filter(|i| i % 4 == 0) { 118 | let Q = constants::EIGHT_TORSION[i].mul_by_pow_2(1); 119 | assert!(Q.is_valid()); 120 | assert!(Q.is_identity()); 121 | } 122 | } 123 | 124 | /// Test that SQRT_M1 is the positive square root of -1 125 | #[test] 126 | fn test_sqrt_minus_one() { 127 | let minus_one = FieldElement::MINUS_ONE; 128 | let sqrt_m1_sq = &constants::SQRT_M1 * &constants::SQRT_M1; 129 | assert_eq!(minus_one, sqrt_m1_sq); 130 | assert!(bool::from(!constants::SQRT_M1.is_negative())); 131 | } 132 | 133 | #[test] 134 | fn test_sqrt_constants_sign() { 135 | let minus_one = FieldElement::MINUS_ONE; 136 | let (was_nonzero_square, invsqrt_m1) = minus_one.invsqrt(); 137 | assert!(bool::from(was_nonzero_square)); 138 | let sign_test_sqrt = &invsqrt_m1 * &constants::SQRT_M1; 139 | assert_eq!(sign_test_sqrt, minus_one); 140 | } 141 | 142 | /// Test that d = -121665/121666 143 | #[test] 144 | #[cfg(all(curve25519_dalek_bits = "32", not(curve25519_dalek_backend = "fiat")))] 145 | fn test_d_vs_ratio() { 146 | use crate::backend::serial::u32::field::FieldElement2625; 147 | let a = -&FieldElement2625([121665, 0, 0, 0, 0, 0, 0, 0, 0, 0]); 148 | let b = FieldElement2625([121666, 0, 0, 0, 0, 0, 0, 0, 0, 0]); 149 | let d = &a * &b.invert(); 150 | let d2 = &d + &d; 151 | assert_eq!(d, constants::EDWARDS_D); 152 | assert_eq!(d2, constants::EDWARDS_D2); 153 | } 154 | 155 | /// Test that d = -121665/121666 156 | #[test] 157 | #[cfg(all(curve25519_dalek_bits = "64", not(curve25519_dalek_backend = "fiat")))] 158 | fn test_d_vs_ratio() { 159 | use crate::backend::serial::u64::field::FieldElement51; 160 | let a = -&FieldElement51([121665, 0, 0, 0, 0]); 161 | let b = FieldElement51([121666, 0, 0, 0, 0]); 162 | let d = &a * &b.invert(); 163 | let d2 = &d + &d; 164 | assert_eq!(d, constants::EDWARDS_D); 165 | assert_eq!(d2, constants::EDWARDS_D2); 166 | } 167 | 168 | #[test] 169 | fn test_sqrt_ad_minus_one() { 170 | let a = FieldElement::MINUS_ONE; 171 | let ad_minus_one = &(&a * &constants::EDWARDS_D) + &a; 172 | let should_be_ad_minus_one = constants::SQRT_AD_MINUS_ONE.square(); 173 | assert_eq!(should_be_ad_minus_one, ad_minus_one); 174 | } 175 | 176 | /// Test that ED25519_SQRTAM2 squared is MONTGOMERY_A_NEG - 2 177 | #[test] 178 | #[cfg(feature = "digest")] 179 | fn test_sqrt_a_minus_2() { 180 | let one = FieldElement::ONE; 181 | let a_minus_two = &(&constants::MONTGOMERY_A_NEG - &one) - &one; 182 | 183 | assert_eq!(constants::ED25519_SQRTAM2.square(), a_minus_two) 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /curve25519-dalek/src/diagnostics.rs: -------------------------------------------------------------------------------- 1 | //! Build time diagnostics 2 | 3 | // auto is assumed or selected 4 | #[cfg(curve25519_dalek_backend = "auto")] 5 | compile_error!("curve25519_dalek_backend is 'auto'"); 6 | 7 | // fiat was overridden 8 | #[cfg(curve25519_dalek_backend = "fiat")] 9 | compile_error!("curve25519_dalek_backend is 'fiat'"); 10 | 11 | // serial was assumed or overridden 12 | #[cfg(curve25519_dalek_backend = "serial")] 13 | compile_error!("curve25519_dalek_backend is 'serial'"); 14 | 15 | // simd was assumed over overridden 16 | #[cfg(curve25519_dalek_backend = "simd")] 17 | compile_error!("curve25519_dalek_backend is 'simd'"); 18 | 19 | // 32 bits target_pointer_width was assumed or overridden 20 | #[cfg(curve25519_dalek_bits = "32")] 21 | compile_error!("curve25519_dalek_bits is '32'"); 22 | 23 | // 64 bits target_pointer_width was assumed or overridden 24 | #[cfg(curve25519_dalek_bits = "64")] 25 | compile_error!("curve25519_dalek_bits is '64'"); 26 | -------------------------------------------------------------------------------- /curve25519-dalek/src/edwards/affine.rs: -------------------------------------------------------------------------------- 1 | use super::{CompressedEdwardsY, EdwardsPoint}; 2 | use crate::traits::Identity; 3 | use crate::{Scalar, field::FieldElement}; 4 | use core::ops::Mul; 5 | use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; 6 | 7 | #[cfg(feature = "zeroize")] 8 | use zeroize::DefaultIsZeroes; 9 | 10 | /// Affine Edwards point on untwisted curve. 11 | #[derive(Copy, Clone, Debug)] 12 | pub struct AffinePoint { 13 | pub(super) x: FieldElement, 14 | pub(super) y: FieldElement, 15 | } 16 | 17 | impl ConstantTimeEq for AffinePoint { 18 | fn ct_eq(&self, other: &Self) -> Choice { 19 | self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y) 20 | } 21 | } 22 | 23 | impl ConditionallySelectable for AffinePoint { 24 | fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { 25 | Self { 26 | x: FieldElement::conditional_select(&a.x, &b.x, choice), 27 | y: FieldElement::conditional_select(&a.y, &b.y, choice), 28 | } 29 | } 30 | } 31 | 32 | impl Default for AffinePoint { 33 | fn default() -> AffinePoint { 34 | AffinePoint::identity() 35 | } 36 | } 37 | 38 | impl Identity for AffinePoint { 39 | fn identity() -> AffinePoint { 40 | AffinePoint { 41 | x: FieldElement::ZERO, 42 | y: FieldElement::ONE, 43 | } 44 | } 45 | } 46 | 47 | impl PartialEq for AffinePoint { 48 | fn eq(&self, other: &Self) -> bool { 49 | self.ct_eq(other).into() 50 | } 51 | } 52 | 53 | impl Eq for AffinePoint {} 54 | 55 | #[cfg(feature = "zeroize")] 56 | impl DefaultIsZeroes for AffinePoint {} 57 | 58 | impl AffinePoint { 59 | /// Convert to extended coordinates. 60 | pub fn to_edwards(self) -> EdwardsPoint { 61 | EdwardsPoint { 62 | X: self.x, 63 | Y: self.y, 64 | Z: FieldElement::ONE, 65 | T: &self.x * &self.y, 66 | } 67 | } 68 | 69 | /// Compress affine Edwards coordinates into `CompressedEdwardsY` format. 70 | #[inline] 71 | pub fn compress(self) -> CompressedEdwardsY { 72 | let mut s = self.y.to_bytes(); 73 | s[31] ^= self.x.is_negative().unwrap_u8() << 7; 74 | CompressedEdwardsY(s) 75 | } 76 | } 77 | 78 | impl Mul for Scalar { 79 | type Output = EdwardsPoint; 80 | 81 | #[inline] 82 | fn mul(self, rhs: AffinePoint) -> EdwardsPoint { 83 | self * &rhs 84 | } 85 | } 86 | 87 | impl Mul<&AffinePoint> for Scalar { 88 | type Output = EdwardsPoint; 89 | 90 | #[inline] 91 | fn mul(self, rhs: &AffinePoint) -> EdwardsPoint { 92 | rhs.to_edwards() * self 93 | } 94 | } 95 | 96 | #[cfg(test)] 97 | mod tests { 98 | use super::{AffinePoint, EdwardsPoint, Identity}; 99 | use crate::constants; 100 | 101 | #[test] 102 | fn identity_conversion() { 103 | assert_eq!( 104 | AffinePoint::identity().to_edwards(), 105 | EdwardsPoint::identity() 106 | ); 107 | } 108 | 109 | #[test] 110 | fn generator_round_trip() { 111 | let basepoint = constants::ED25519_BASEPOINT_POINT; 112 | assert_eq!(basepoint.to_affine().to_edwards(), basepoint); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /curve25519-dalek/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | #![no_std] 13 | #![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))] 14 | #![cfg_attr(docsrs, doc(cfg_hide(docsrs)))] 15 | //------------------------------------------------------------------------ 16 | // Documentation: 17 | //------------------------------------------------------------------------ 18 | #![doc( 19 | html_logo_url = "https://cdn.jsdelivr.net/gh/dalek-cryptography/curve25519-dalek/docs/assets/dalek-logo-clear.png" 20 | )] 21 | #![doc = include_str!("../README.md")] 22 | //------------------------------------------------------------------------ 23 | // Linting: 24 | //------------------------------------------------------------------------ 25 | #![cfg_attr(allow_unused_unsafe, allow(unused_unsafe))] 26 | #![warn( 27 | clippy::unwrap_used, 28 | missing_docs, 29 | rust_2018_idioms, 30 | unused_lifetimes, 31 | unused_qualifications 32 | )] 33 | 34 | //------------------------------------------------------------------------ 35 | // External dependencies: 36 | //------------------------------------------------------------------------ 37 | 38 | #[cfg(feature = "alloc")] 39 | #[allow(unused_imports)] 40 | #[macro_use] 41 | extern crate alloc; 42 | 43 | // TODO: move std-dependent tests to `tests/` 44 | #[cfg(test)] 45 | #[macro_use] 46 | extern crate std; 47 | 48 | #[cfg(feature = "digest")] 49 | pub use digest; 50 | 51 | // Internal macros. Must come first! 52 | #[macro_use] 53 | pub(crate) mod macros; 54 | 55 | //------------------------------------------------------------------------ 56 | // curve25519-dalek public modules 57 | //------------------------------------------------------------------------ 58 | 59 | // Scalar arithmetic mod l = 2^252 + ..., the order of the Ristretto group 60 | pub mod scalar; 61 | 62 | // Point operations on the Montgomery form of Curve25519 63 | pub mod montgomery; 64 | 65 | // Point operations on the Edwards form of Curve25519 66 | pub mod edwards; 67 | 68 | // Group operations on the Ristretto group 69 | pub mod ristretto; 70 | 71 | // Useful constants, like the Ed25519 basepoint 72 | pub mod constants; 73 | 74 | // External (and internal) traits. 75 | pub mod traits; 76 | 77 | //------------------------------------------------------------------------ 78 | // curve25519-dalek internal modules 79 | //------------------------------------------------------------------------ 80 | 81 | // Finite field arithmetic mod p = 2^255 - 19 82 | pub(crate) mod field; 83 | 84 | // Arithmetic backends (using u32, u64, etc) live here 85 | #[cfg(docsrs)] 86 | pub mod backend; 87 | #[cfg(not(docsrs))] 88 | pub(crate) mod backend; 89 | 90 | // Generic code for window lookups 91 | pub(crate) mod window; 92 | 93 | #[cfg(feature = "lizard")] 94 | pub mod lizard; 95 | 96 | pub use crate::{ 97 | edwards::EdwardsPoint, montgomery::MontgomeryPoint, ristretto::RistrettoPoint, scalar::Scalar, 98 | }; 99 | 100 | // Build time diagnostics for validation 101 | #[cfg(curve25519_dalek_diagnostics = "build")] 102 | mod diagnostics; 103 | -------------------------------------------------------------------------------- /curve25519-dalek/src/lizard/jacobi_quartic.rs: -------------------------------------------------------------------------------- 1 | //! Helper functions for use with Lizard 2 | 3 | #![allow(non_snake_case)] 4 | 5 | use subtle::{ConditionallyNegatable, ConditionallySelectable, ConstantTimeEq, CtOption}; 6 | 7 | use super::lizard_constants; 8 | use crate::constants; 9 | 10 | use crate::field::FieldElement; 11 | 12 | /// Represents a point (s,t) on the the Jacobi quartic associated 13 | /// to the Edwards curve. 14 | #[derive(Copy, Clone)] 15 | #[allow(missing_docs)] 16 | pub struct JacobiPoint { 17 | pub S: FieldElement, 18 | pub T: FieldElement, 19 | } 20 | 21 | impl JacobiPoint { 22 | /// Elligator2 is defined in two steps: first a function `e` maps a field element `x` 23 | /// to a point on the Jacobi quartic associated to the Edwards curve. 24 | /// Then this point is mapped to a point on the Edwards curve. 25 | /// Note `e` maps `x` and `-x` to the same point. 26 | /// This function computes a positive field element that is mapped by `e` to a given point, 27 | /// if it exists. The other inverse is the negative of the return value. 28 | pub(crate) fn e_inv_positive(&self) -> CtOption { 29 | let mut out = FieldElement::ZERO; 30 | 31 | // Special case: s = 0. If s is zero, either t = 1 or t = -1. 32 | // If t=1, then sqrt(i*d) is the preimage. Otherwise it's 0. 33 | let s_is_zero = self.S.is_zero(); 34 | let t_equals_one = self.T.ct_eq(&FieldElement::ONE); 35 | out.conditional_assign(&lizard_constants::SQRT_ID, t_equals_one); 36 | let mut is_defined = s_is_zero; 37 | let mut done = s_is_zero; 38 | 39 | // a := (t+1) (d+1)/(d-1) 40 | let a = &(&self.T + &FieldElement::ONE) * &lizard_constants::DP1_OVER_DM1; 41 | let a2 = a.square(); 42 | 43 | // y := 1/sqrt(i (s^4 - a^2)). 44 | let s2 = self.S.square(); 45 | let s4 = s2.square(); 46 | let invSqY = &(&s4 - &a2) * &constants::SQRT_M1; 47 | 48 | // There is no preimage if the square root of i*(s^4-a^2) does not exist. 49 | let (sq, y) = invSqY.invsqrt(); 50 | is_defined |= sq; 51 | done |= !sq; 52 | 53 | // x := (a + sign(s)*s^2) y 54 | let mut pms2 = s2; 55 | pms2.conditional_negate(self.S.is_negative()); 56 | let mut x = &(&a + &pms2) * &y; 57 | // Always pick the positive solution 58 | let x_is_negative = x.is_negative(); 59 | x.conditional_negate(x_is_negative); 60 | out.conditional_assign(&x, !done); 61 | 62 | CtOption::new(out, is_defined) 63 | } 64 | 65 | pub(crate) fn dual(&self) -> JacobiPoint { 66 | JacobiPoint { 67 | S: -(&self.S), 68 | T: -(&self.T), 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /curve25519-dalek/src/lizard/lizard_constants.rs: -------------------------------------------------------------------------------- 1 | //! Constants for use in Lizard 2 | //! 3 | //! Could be moved into backend/serial/u??/constants.rs 4 | 5 | #[cfg(curve25519_dalek_bits = "64")] 6 | pub(crate) use super::u64_constants::*; 7 | 8 | #[cfg(curve25519_dalek_bits = "32")] 9 | pub(crate) use super::u32_constants::*; 10 | 11 | // ------------------------------------------------------------------------ 12 | // Tests 13 | // ------------------------------------------------------------------------ 14 | 15 | #[cfg(test)] 16 | mod test { 17 | 18 | use super::*; 19 | use crate::constants; 20 | use crate::field::FieldElement; 21 | 22 | #[test] 23 | fn test_lizard_constants() { 24 | let (_, sqrt_id) = FieldElement::sqrt_ratio_i( 25 | &(&constants::SQRT_M1 * &constants::EDWARDS_D), 26 | &FieldElement::ONE, 27 | ); 28 | assert_eq!(sqrt_id, SQRT_ID); 29 | 30 | assert_eq!( 31 | &(&constants::EDWARDS_D + &FieldElement::ONE) 32 | * &(&constants::EDWARDS_D - &FieldElement::ONE).invert(), 33 | DP1_OVER_DM1 34 | ); 35 | 36 | assert_eq!( 37 | MDOUBLE_INVSQRT_A_MINUS_D, 38 | -&(&constants::INVSQRT_A_MINUS_D + &constants::INVSQRT_A_MINUS_D) 39 | ); 40 | 41 | assert_eq!( 42 | MIDOUBLE_INVSQRT_A_MINUS_D, 43 | &MDOUBLE_INVSQRT_A_MINUS_D * &constants::SQRT_M1 44 | ); 45 | 46 | let (_, invsqrt_one_plus_d) = (&constants::EDWARDS_D + &FieldElement::ONE).invsqrt(); 47 | assert_eq!(-&invsqrt_one_plus_d, MINVSQRT_ONE_PLUS_D); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /curve25519-dalek/src/lizard/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Lizard method for encoding/decoding 16 bytes into Ristretto points. 2 | 3 | #![allow(non_snake_case)] 4 | 5 | #[cfg(curve25519_dalek_bits = "32")] 6 | mod u32_constants; 7 | 8 | #[cfg(curve25519_dalek_bits = "64")] 9 | mod u64_constants; 10 | 11 | mod jacobi_quartic; 12 | mod lizard_constants; 13 | pub mod lizard_ristretto; 14 | -------------------------------------------------------------------------------- /curve25519-dalek/src/lizard/u32_constants.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | 3 | cfg_if! { 4 | if #[cfg(curve25519_dalek_backend = "fiat")] { 5 | pub use crate::backend::serial::fiat_u32::field::FieldElement2625; 6 | 7 | const fn field_element(element: [u32; 10]) -> FieldElement2625 { 8 | FieldElement2625(fiat_crypto::curve25519_32::fiat_25519_tight_field_element(element)) 9 | } 10 | } else { 11 | pub use crate::backend::serial::u32::field::FieldElement2625; 12 | 13 | const fn field_element(element: [u32; 10]) -> FieldElement2625 { 14 | FieldElement2625(element) 15 | } 16 | } 17 | } 18 | 19 | /// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter. 20 | pub const SQRT_ID: FieldElement2625 = field_element([ 21 | 39590824, 701138, 28659366, 23623507, 53932708, 32206357, 36326585, 24309414, 26167230, 1494357, 22 | ]); 23 | 24 | /// `= (d+1)/(d-1)`, where `d` is the Edwards curve parameter. 25 | pub const DP1_OVER_DM1: FieldElement2625 = field_element([ 26 | 58833708, 32184294, 62457071, 26110240, 19032991, 27203620, 7122892, 18068959, 51019405, 27 | 3776288, 28 | ]); 29 | 30 | /// `= -2/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters. 31 | pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = field_element([ 32 | 54885894, 25242303, 55597453, 9067496, 51808079, 33312638, 25456129, 14121551, 54921728, 33 | 3972023, 34 | ]); 35 | 36 | /// `= -2i/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters 37 | /// and `i = +sqrt(-1)`. 38 | pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement2625 = field_element([ 39 | 58178520, 23970840, 26444491, 29801899, 41064376, 743696, 2900628, 27920316, 41968995, 5270573, 40 | ]); 41 | 42 | /// `= -1/sqrt(1+d)`, where `d` is the Edwards curve parameters. 43 | pub const MINVSQRT_ONE_PLUS_D: FieldElement2625 = field_element([ 44 | 38019585, 4791795, 20332186, 18653482, 46576675, 33182583, 65658549, 2817057, 12569934, 45 | 30919145, 46 | ]); 47 | -------------------------------------------------------------------------------- /curve25519-dalek/src/lizard/u64_constants.rs: -------------------------------------------------------------------------------- 1 | use cfg_if::cfg_if; 2 | 3 | cfg_if! { 4 | if #[cfg(curve25519_dalek_backend = "fiat")] { 5 | pub use crate::backend::serial::fiat_u64::field::FieldElement51; 6 | 7 | const fn field_element(element: [u64; 5]) -> FieldElement51 { 8 | FieldElement51(fiat_crypto::curve25519_64::fiat_25519_tight_field_element(element)) 9 | } 10 | } else { 11 | pub use crate::backend::serial::u64::field::FieldElement51; 12 | 13 | const fn field_element(element: [u64; 5]) -> FieldElement51 { 14 | FieldElement51(element) 15 | } 16 | } 17 | } 18 | 19 | /// `= sqrt(i*d)`, where `i = +sqrt(-1)` and `d` is the Edwards curve parameter. 20 | pub const SQRT_ID: FieldElement51 = field_element([ 21 | 2298852427963285, 22 | 3837146560810661, 23 | 4413131899466403, 24 | 3883177008057528, 25 | 2352084440532925, 26 | ]); 27 | 28 | /// `= (d+1)/(d-1)`, where `d` is the Edwards curve parameter. 29 | pub const DP1_OVER_DM1: FieldElement51 = field_element([ 30 | 2159851467815724, 31 | 1752228607624431, 32 | 1825604053920671, 33 | 1212587319275468, 34 | 253422448836237, 35 | ]); 36 | 37 | /// `= -2/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters. 38 | pub const MDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = field_element([ 39 | 1693982333959686, 40 | 608509411481997, 41 | 2235573344831311, 42 | 947681270984193, 43 | 266558006233600, 44 | ]); 45 | 46 | /// `= -2i/sqrt(a-d)`, where `a = -1 (mod p)`, `d` are the Edwards curve parameters 47 | /// and `i = +sqrt(-1)`. 48 | pub const MIDOUBLE_INVSQRT_A_MINUS_D: FieldElement51 = field_element([ 49 | 1608655899704280, 50 | 1999971613377227, 51 | 49908634785720, 52 | 1873700692181652, 53 | 353702208628067, 54 | ]); 55 | 56 | /// `= -1/sqrt(1+d)`, where `d` is the Edwards curve parameters. 57 | pub const MINVSQRT_ONE_PLUS_D: FieldElement51 = field_element([ 58 | 321571956990465, 59 | 1251814006996634, 60 | 2226845496292387, 61 | 189049560751797, 62 | 2074948709371214, 63 | ]); 64 | -------------------------------------------------------------------------------- /curve25519-dalek/src/macros.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of curve25519-dalek. 4 | // Copyright (c) 2016-2021 isis agora lovecruft 5 | // Copyright (c) 2016-2019 Henry de Valence 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - Henry de Valence 11 | 12 | //! Internal macros. 13 | 14 | /// Define borrow and non-borrow variants of `Add`. 15 | macro_rules! define_add_variants { 16 | (LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { 17 | impl<'b> Add<&'b $rhs> for $lhs { 18 | type Output = $out; 19 | fn add(self, rhs: &'b $rhs) -> $out { 20 | &self + rhs 21 | } 22 | } 23 | 24 | impl<'a> Add<$rhs> for &'a $lhs { 25 | type Output = $out; 26 | fn add(self, rhs: $rhs) -> $out { 27 | self + &rhs 28 | } 29 | } 30 | 31 | impl Add<$rhs> for $lhs { 32 | type Output = $out; 33 | fn add(self, rhs: $rhs) -> $out { 34 | &self + &rhs 35 | } 36 | } 37 | }; 38 | } 39 | 40 | /// Define non-borrow variants of `AddAssign`. 41 | macro_rules! define_add_assign_variants { 42 | (LHS = $lhs:ty, RHS = $rhs:ty) => { 43 | impl AddAssign<$rhs> for $lhs { 44 | fn add_assign(&mut self, rhs: $rhs) { 45 | *self += &rhs; 46 | } 47 | } 48 | }; 49 | } 50 | 51 | /// Define borrow and non-borrow variants of `Sub`. 52 | macro_rules! define_sub_variants { 53 | (LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { 54 | impl<'b> Sub<&'b $rhs> for $lhs { 55 | type Output = $out; 56 | fn sub(self, rhs: &'b $rhs) -> $out { 57 | &self - rhs 58 | } 59 | } 60 | 61 | impl<'a> Sub<$rhs> for &'a $lhs { 62 | type Output = $out; 63 | fn sub(self, rhs: $rhs) -> $out { 64 | self - &rhs 65 | } 66 | } 67 | 68 | impl Sub<$rhs> for $lhs { 69 | type Output = $out; 70 | fn sub(self, rhs: $rhs) -> $out { 71 | &self - &rhs 72 | } 73 | } 74 | }; 75 | } 76 | 77 | /// Define non-borrow variants of `SubAssign`. 78 | macro_rules! define_sub_assign_variants { 79 | (LHS = $lhs:ty, RHS = $rhs:ty) => { 80 | impl SubAssign<$rhs> for $lhs { 81 | fn sub_assign(&mut self, rhs: $rhs) { 82 | *self -= &rhs; 83 | } 84 | } 85 | }; 86 | } 87 | 88 | /// Define borrow and non-borrow variants of `Mul`. 89 | macro_rules! define_mul_variants { 90 | (LHS = $lhs:ty, RHS = $rhs:ty, Output = $out:ty) => { 91 | impl<'b> Mul<&'b $rhs> for $lhs { 92 | type Output = $out; 93 | fn mul(self, rhs: &'b $rhs) -> $out { 94 | &self * rhs 95 | } 96 | } 97 | 98 | impl<'a> Mul<$rhs> for &'a $lhs { 99 | type Output = $out; 100 | fn mul(self, rhs: $rhs) -> $out { 101 | self * &rhs 102 | } 103 | } 104 | 105 | impl Mul<$rhs> for $lhs { 106 | type Output = $out; 107 | fn mul(self, rhs: $rhs) -> $out { 108 | &self * &rhs 109 | } 110 | } 111 | }; 112 | } 113 | 114 | /// Define non-borrow variants of `MulAssign`. 115 | macro_rules! define_mul_assign_variants { 116 | (LHS = $lhs:ty, RHS = $rhs:ty) => { 117 | impl MulAssign<$rhs> for $lhs { 118 | fn mul_assign(&mut self, rhs: $rhs) { 119 | *self *= &rhs; 120 | } 121 | } 122 | }; 123 | } 124 | -------------------------------------------------------------------------------- /curve25519-dalek/tests/build_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | function match_and_report() { 4 | PATTERN=$1 5 | FILE=$2 6 | 7 | if grep -q "$PATTERN" "$FILE"; then 8 | echo build OK "$FILE" : "$PATTERN" 9 | else 10 | echo build ERROR "$FILE" : "$PATTERN" 11 | echo ">>>>>>>>>>>>>>>>>>>>>>>>>>" 12 | cat "$FILE" 13 | echo "<<<<<<<<<<<<<<<<<<<<<<<<<<" 14 | exit 1 15 | fi 16 | } 17 | 18 | # Assuming naively 64 bit host 19 | cargo clean 20 | OUT=build_1.txt 21 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\"" cargo build > "$OUT" 2>&1 22 | match_and_report "curve25519_dalek_backend is 'simd'" "$OUT" 23 | match_and_report "curve25519_dalek_bits is '64'" "$OUT" 24 | 25 | # Override to 32 bits assuming naively 64 bit build host 26 | cargo clean 27 | OUT=build_2.txt 28 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_bits=\"32\"" cargo build > "$OUT" 2>&1 29 | match_and_report "curve25519_dalek_backend is 'serial'" "$OUT" 30 | match_and_report "curve25519_dalek_bits is '32'" "$OUT" 31 | 32 | # Override to 64 bits on 32 bit target 33 | cargo clean 34 | OUT=build_3.txt 35 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_bits=\"64\"" cargo build --target i686-unknown-linux-gnu > "$OUT" 2>&1 36 | match_and_report "curve25519_dalek_backend is 'serial'" "$OUT" 37 | match_and_report "curve25519_dalek_bits is '64'" "$OUT" 38 | 39 | # 32 bit target default 40 | cargo clean 41 | OUT=build_4.txt 42 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\"" cargo build --target i686-unknown-linux-gnu > "$OUT" 2>&1 43 | match_and_report "curve25519_dalek_backend is 'serial'" "$OUT" 44 | match_and_report "curve25519_dalek_bits is '32'" "$OUT" 45 | 46 | # wasm 32 bit target default 47 | cargo clean 48 | OUT=build_5.txt 49 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\"" cargo build --target wasm32-unknown-unknown > "$OUT" 2>&1 50 | match_and_report "curve25519_dalek_backend is 'serial'" "$OUT" 51 | match_and_report "curve25519_dalek_bits is '32'" "$OUT" 52 | 53 | # wasm 32 bit target default 54 | # Attempted override w/ "simd" should result "serial" addition 55 | cargo clean 56 | OUT=build_5_1.txt 57 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"simd\"" cargo build --target wasm32-unknown-unknown > "$OUT" 2>&1 58 | # This override must fail the compilation since "simd" is not available 59 | # See: issues/532 60 | match_and_report "Could not override curve25519_dalek_backend to simd" "$OUT" 61 | 62 | # fiat override with default 64 bit naive host assumption 63 | cargo clean 64 | OUT=build_6.txt 65 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"fiat\"" cargo build > "$OUT" 2>&1 66 | match_and_report "curve25519_dalek_backend is 'fiat'" "$OUT" 67 | match_and_report "curve25519_dalek_bits is '64'" "$OUT" 68 | 69 | # fiat 32 bit override 70 | cargo clean 71 | OUT=build_7.txt 72 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"fiat\" --cfg curve25519_dalek_bits=\"32\"" cargo build > "$OUT" 2>&1 73 | match_and_report "curve25519_dalek_backend is 'fiat'" "$OUT" 74 | match_and_report "curve25519_dalek_bits is '32'" "$OUT" 75 | 76 | # serial override with default 64 bit naive host assumption 77 | cargo clean 78 | OUT=build_8.txt 79 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"serial\"" cargo build > "$OUT" 2>&1 80 | match_and_report "curve25519_dalek_backend is 'serial'" "$OUT" 81 | match_and_report "curve25519_dalek_bits is '64'" "$OUT" 82 | 83 | # serial 32 bit override 84 | cargo clean 85 | OUT=build_9.txt 86 | env RUSTFLAGS="--cfg curve25519_dalek_diagnostics=\"build\" --cfg curve25519_dalek_backend=\"serial\" --cfg curve25519_dalek_bits=\"32\"" cargo build > "$OUT" 2>&1 87 | match_and_report "curve25519_dalek_backend is 'serial'" "$OUT" 88 | match_and_report "curve25519_dalek_bits is '32'" "$OUT" 89 | -------------------------------------------------------------------------------- /docs/assets/dalek-logo-clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/curve25519-dalek/b76b924080dad8bd07bc26f00bf2d4e4fc5daed1/docs/assets/dalek-logo-clear.png -------------------------------------------------------------------------------- /docs/assets/dalek-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/curve25519-dalek/b76b924080dad8bd07bc26f00bf2d4e4fc5daed1/docs/assets/dalek-logo.png -------------------------------------------------------------------------------- /ed25519-dalek/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | 3 | .cargo 4 | 5 | *~ 6 | \#* 7 | .\#* 8 | *.swp 9 | *.orig 10 | *.bak 11 | 12 | *.s 13 | -------------------------------------------------------------------------------- /ed25519-dalek/.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - stable 5 | - beta 6 | - nightly 7 | 8 | env: 9 | - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='' 10 | 11 | matrix: 12 | include: 13 | # We use the 64-bit optimised curve backend by default, so also test with 14 | # the 32-bit backend (this also exercises testing with `no_std`): 15 | - rust: nightly 16 | env: TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend alloc' 17 | # Also test the batch feature: 18 | - rust: nightly 19 | env: TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u64_backend alloc batch' 20 | # Test any nightly gated features on nightly: 21 | - rust: nightly 22 | env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly' 23 | # Test serde support on stable, assuming that if it works there it'll work everywhere: 24 | - rust: stable 25 | env: TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='serde' 26 | 27 | script: 28 | - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS 29 | 30 | notifications: 31 | slack: 32 | rooms: 33 | - dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots 34 | -------------------------------------------------------------------------------- /ed25519-dalek/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 5 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 6 | 7 | Entries are listed in reverse chronological order per undeprecated major series. 8 | 9 | # 3.x series 10 | 11 | ## 3.0.0-pre.0 12 | 13 | * Update edition to 2024 14 | * Update the MSRV from 1.60 to 1.85 15 | * Update `ed25519` and `signature` deps 16 | * Remove `std` feature 17 | * Make signing and verifying keys use `pkcs8::spki::SignatureAlgorithmIdentifier` instead of `DynSignatureAlgorithmIdentifier` 18 | 19 | # 2.x series 20 | 21 | ## 2.2.0 22 | 23 | * Add `hazmat`-gated methods `SigningKey::verify_stream()` and `VerifyingKey::verify_stream()` 24 | * Add `Debug` and `Eq` traits for `hazmat::ExpandedSecretKey` 25 | 26 | ## 2.1.1 27 | 28 | * Fix nightly SIMD build 29 | 30 | ## 2.1.0 31 | 32 | * Add `SigningKey::to_scalar_bytes` for getting the unclamped scalar from a signing key 33 | * Loosened `signature` dependency to allow version 2.2 34 | 35 | ## 2.0.0 36 | 37 | ### Breaking changes 38 | 39 | * Bump MSRV from 1.41 to 1.60.0 40 | * Bump Rust edition 41 | * Bump `signature` dependency to 2.0 42 | * Make `digest` an optional dependency 43 | * Make `zeroize` an optional dependency 44 | * Make `rand_core` an optional dependency 45 | * [curve25519 backends] are now automatically selected 46 | * [curve25519 backends] are now overridable via cfg instead of using additive features 47 | * Make all batch verification deterministic remove `batch_deterministic` (PR [#256](https://github.com/dalek-cryptography/ed25519-dalek/pull/256)) 48 | * Rename `Keypair` → `SigningKey` and `PublicKey` → `VerifyingKey` 49 | * Remove default-public `ExpandedSecretKey` API (PR [#205](https://github.com/dalek-cryptography/ed25519-dalek/pull/205)) 50 | * Make `hazmat` feature to expose `ExpandedSecretKey`, `raw_sign()`, `raw_sign_prehashed()`, `raw_verify()`, and `raw_verify_prehashed()` 51 | 52 | [curve25519 backends]: https://github.com/dalek-cryptography/curve25519-dalek/#backends 53 | 54 | ### Other changes 55 | 56 | * Add `Context` type for prehashed signing 57 | * Add `VerifyingKey::{verify_prehash_strict, is_weak}` 58 | * Add `pkcs` feature to support PKCS #8 (de)serialization of `SigningKey` and `VerifyingKey` 59 | * Add `fast` feature to include basepoint tables 60 | * Add tests for validation criteria 61 | * Impl `DigestSigner`/`DigestVerifier` for `SigningKey`/`VerifyingKey`, respectively 62 | * Impl `Hash` for `VerifyingKey` 63 | * Impl `Clone`, `Drop`, and `ZeroizeOnDrop` for `SigningKey` 64 | * Remove `rand` dependency 65 | * Improve key deserialization diagnostics 66 | -------------------------------------------------------------------------------- /ed25519-dalek/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ed25519-dalek" 3 | version = "3.0.0-pre.1" 4 | edition = "2024" 5 | authors = [ 6 | "isis lovecruft ", 7 | "Tony Arcieri ", 8 | "Michael Rosenberg ", 9 | ] 10 | readme = "README.md" 11 | license = "BSD-3-Clause" 12 | repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/ed25519-dalek" 13 | homepage = "https://github.com/dalek-cryptography/curve25519-dalek" 14 | documentation = "https://docs.rs/ed25519-dalek" 15 | keywords = ["cryptography", "ed25519", "curve25519", "signature", "ECC"] 16 | categories = ["cryptography", "no-std"] 17 | description = "Fast and efficient ed25519 EdDSA key generations, signing, and verification in pure Rust." 18 | exclude = [".gitignore", "TESTVECTORS", "VALIDATIONVECTORS", "res/*"] 19 | rust-version = "1.85" 20 | 21 | [package.metadata.docs.rs] 22 | rustdoc-args = [ 23 | "--html-in-header", 24 | "docs/assets/rustdoc-include-katex-header.html", 25 | "--cfg", 26 | "docsrs", 27 | ] 28 | features = ["batch", "digest", "hazmat", "pem", "serde"] 29 | 30 | [dependencies] 31 | curve25519-dalek = { version = "=5.0.0-pre.1", default-features = false, features = [ 32 | "digest", 33 | ] } 34 | ed25519 = { version = "3.0.0-rc.0", default-features = false } 35 | signature = { version = "3.0.0-rc.4", optional = true, default-features = false } 36 | sha2 = { version = "0.11.0-rc.2", default-features = false } 37 | subtle = { version = "2.3.0", default-features = false } 38 | 39 | # optional features 40 | keccak = { version = "0.2.0-rc.0", default-features = false, optional = true } 41 | rand_core = { version = "0.9", default-features = false, optional = true } 42 | serde = { version = "1.0", default-features = false, optional = true } 43 | zeroize = { version = "1.5", default-features = false, optional = true } 44 | 45 | [dev-dependencies] 46 | curve25519-dalek = { version = "=5.0.0-pre.1", default-features = false, features = [ 47 | "digest", 48 | "rand_core", 49 | ] } 50 | x25519-dalek = { version = "=3.0.0-pre.1", default-features = false, features = [ 51 | "static_secrets", 52 | ] } 53 | blake2 = "0.11.0-rc.2" 54 | sha3 = "0.11.0-rc.3" 55 | hex = "0.4" 56 | bincode = "1.0" 57 | serde_json = "1.0" 58 | criterion = { version = "0.5", features = ["html_reports"] } 59 | hex-literal = "0.4" 60 | rand = "0.9" 61 | rand_core = { version = "0.9", default-features = false } 62 | rand_chacha = "0.9" 63 | serde = { version = "1.0", features = ["derive"] } 64 | strobe-rs = "0.5" 65 | toml = { version = "0.9" } 66 | 67 | [[bench]] 68 | name = "ed25519_benchmarks" 69 | harness = false 70 | required-features = ["rand_core"] 71 | 72 | [features] 73 | default = ["fast", "zeroize"] 74 | alloc = [ 75 | "curve25519-dalek/alloc", 76 | "ed25519/alloc", 77 | "signature/alloc", 78 | "serde?/alloc", 79 | "zeroize?/alloc", 80 | ] 81 | 82 | batch = ["alloc", "dep:keccak", "rand_core"] 83 | fast = ["curve25519-dalek/precomputed-tables"] 84 | digest = ["signature/digest"] 85 | # Exposes the hazmat module 86 | hazmat = [] 87 | # Turns off stricter checking for scalar malleability in signatures 88 | legacy_compatibility = ["curve25519-dalek/legacy_compatibility"] 89 | pkcs8 = ["ed25519/pkcs8"] 90 | pem = ["alloc", "ed25519/pem", "pkcs8"] 91 | rand_core = ["dep:rand_core"] 92 | serde = ["dep:serde", "ed25519/serde"] 93 | zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] 94 | -------------------------------------------------------------------------------- /ed25519-dalek/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2019 isis agora lovecruft. All rights reserved. 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | 1. Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | 10 | 2. Redistributions in binary form must reproduce the above copyright 11 | notice, this list of conditions and the following disclaimer in the 12 | documentation and/or other materials provided with the distribution. 13 | 14 | 3. Neither the name of the copyright holder nor the names of its 15 | contributors may be used to endorse or promote products derived from 16 | this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 19 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 21 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 24 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 25 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 26 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 27 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /ed25519-dalek/benches/ed25519_benchmarks.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of ed25519-dalek. 4 | // Copyright (c) 2018-2019 isis lovecruft 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | use criterion::{Criterion, criterion_group}; 11 | 12 | mod ed25519_benches { 13 | use super::*; 14 | use ed25519_dalek::Signature; 15 | use ed25519_dalek::Signer; 16 | use ed25519_dalek::SigningKey; 17 | use rand::prelude::ThreadRng; 18 | use rand::rng; 19 | 20 | fn sign(c: &mut Criterion) { 21 | let mut csprng: ThreadRng = rng(); 22 | let keypair: SigningKey = SigningKey::generate(&mut csprng); 23 | let msg: &[u8] = b""; 24 | 25 | c.bench_function("Ed25519 signing", move |b| b.iter(|| keypair.sign(msg))); 26 | } 27 | 28 | fn verify(c: &mut Criterion) { 29 | let mut csprng: ThreadRng = rng(); 30 | let keypair: SigningKey = SigningKey::generate(&mut csprng); 31 | let msg: &[u8] = b""; 32 | let sig: Signature = keypair.sign(msg); 33 | 34 | c.bench_function("Ed25519 signature verification", move |b| { 35 | b.iter(|| keypair.verify(msg, &sig)) 36 | }); 37 | } 38 | 39 | fn verify_strict(c: &mut Criterion) { 40 | let mut csprng: ThreadRng = rng(); 41 | let keypair: SigningKey = SigningKey::generate(&mut csprng); 42 | let msg: &[u8] = b""; 43 | let sig: Signature = keypair.sign(msg); 44 | 45 | c.bench_function("Ed25519 strict signature verification", move |b| { 46 | b.iter(|| keypair.verify_strict(msg, &sig)) 47 | }); 48 | } 49 | 50 | #[cfg(feature = "batch")] 51 | fn verify_batch_signatures(c: &mut Criterion) { 52 | use ed25519_dalek::verify_batch; 53 | 54 | static BATCH_SIZES: [usize; 8] = [4, 8, 16, 32, 64, 96, 128, 256]; 55 | 56 | // Benchmark batch verification for all the above batch sizes 57 | let mut group = c.benchmark_group("Ed25519 batch signature verification"); 58 | for size in BATCH_SIZES { 59 | let name = format!("size={size}"); 60 | group.bench_function(name, |b| { 61 | let mut csprng: ThreadRng = rng(); 62 | let keypairs: Vec = (0..size) 63 | .map(|_| SigningKey::generate(&mut csprng)) 64 | .collect(); 65 | let msg: &[u8] = b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; 66 | let messages: Vec<&[u8]> = (0..size).map(|_| msg).collect(); 67 | let signatures: Vec = keypairs.iter().map(|key| key.sign(msg)).collect(); 68 | let verifying_keys: Vec<_> = 69 | keypairs.iter().map(|key| key.verifying_key()).collect(); 70 | 71 | b.iter(|| verify_batch(&messages[..], &signatures[..], &verifying_keys[..])); 72 | }); 73 | } 74 | } 75 | 76 | // If the above function isn't defined, make a placeholder function 77 | #[cfg(not(feature = "batch"))] 78 | fn verify_batch_signatures(_: &mut Criterion) {} 79 | 80 | fn key_generation(c: &mut Criterion) { 81 | let mut csprng: ThreadRng = rng(); 82 | 83 | c.bench_function("Ed25519 keypair generation", move |b| { 84 | b.iter(|| SigningKey::generate(&mut csprng)) 85 | }); 86 | } 87 | 88 | criterion_group! { 89 | name = ed25519_benches; 90 | config = Criterion::default(); 91 | targets = 92 | sign, 93 | verify, 94 | verify_strict, 95 | verify_batch_signatures, 96 | key_generation, 97 | } 98 | } 99 | 100 | criterion::criterion_main!(ed25519_benches::ed25519_benches); 101 | -------------------------------------------------------------------------------- /ed25519-dalek/docs/assets/ed25519-malleability.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/curve25519-dalek/b76b924080dad8bd07bc26f00bf2d4e4fc5daed1/ed25519-dalek/docs/assets/ed25519-malleability.png -------------------------------------------------------------------------------- /ed25519-dalek/docs/assets/rustdoc-include-katex-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 13 | -------------------------------------------------------------------------------- /ed25519-dalek/src/batch/strobe.rs: -------------------------------------------------------------------------------- 1 | //! Minimal implementation of (parts of) Strobe. 2 | 3 | use core::ops::{Deref, DerefMut}; 4 | 5 | #[cfg(feature = "zeroize")] 6 | use zeroize::Zeroize; 7 | 8 | /// Strobe R value; security level 128 is hardcoded 9 | const STROBE_R: u8 = 166; 10 | 11 | const FLAG_I: u8 = 1; 12 | const FLAG_A: u8 = 1 << 1; 13 | const FLAG_C: u8 = 1 << 2; 14 | const FLAG_T: u8 = 1 << 3; 15 | const FLAG_M: u8 = 1 << 4; 16 | const FLAG_K: u8 = 1 << 5; 17 | 18 | fn transmute_state(st: &mut AlignedKeccakState) -> &mut [u64; 25] { 19 | unsafe { &mut *(st as *mut AlignedKeccakState as *mut [u64; 25]) } 20 | } 21 | 22 | /// This is a wrapper around 200-byte buffer that's always 8-byte aligned 23 | /// to make pointers to it safely convertible to pointers to [u64; 25] 24 | /// (since u64 words must be 8-byte aligned) 25 | #[derive(Clone)] 26 | #[repr(align(8))] 27 | struct AlignedKeccakState([u8; 200]); 28 | 29 | #[cfg(feature = "zeroize")] 30 | impl Drop for AlignedKeccakState { 31 | fn drop(&mut self) { 32 | self.0.zeroize(); 33 | } 34 | } 35 | 36 | /// A Strobe context for the 128-bit security level. 37 | /// 38 | /// Only `meta-AD`, `AD`, `KEY`, and `PRF` operations are supported. 39 | #[derive(Clone)] 40 | pub struct Strobe128 { 41 | state: AlignedKeccakState, 42 | pos: u8, 43 | pos_begin: u8, 44 | cur_flags: u8, 45 | } 46 | 47 | impl ::core::fmt::Debug for Strobe128 { 48 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 49 | // Ensure that the Strobe state isn't accidentally logged 50 | write!(f, "Strobe128: STATE OMITTED") 51 | } 52 | } 53 | 54 | impl Strobe128 { 55 | pub fn new(protocol_label: &[u8]) -> Strobe128 { 56 | let initial_state = { 57 | let mut st = AlignedKeccakState([0u8; 200]); 58 | st[0..6].copy_from_slice(&[1, STROBE_R + 2, 1, 0, 1, 96]); 59 | st[6..18].copy_from_slice(b"STROBEv1.0.2"); 60 | keccak::f1600(transmute_state(&mut st)); 61 | 62 | st 63 | }; 64 | 65 | let mut strobe = Strobe128 { 66 | state: initial_state, 67 | pos: 0, 68 | pos_begin: 0, 69 | cur_flags: 0, 70 | }; 71 | 72 | strobe.meta_ad(protocol_label, false); 73 | 74 | strobe 75 | } 76 | 77 | pub fn meta_ad(&mut self, data: &[u8], more: bool) { 78 | self.begin_op(FLAG_M | FLAG_A, more); 79 | self.absorb(data); 80 | } 81 | 82 | pub fn ad(&mut self, data: &[u8], more: bool) { 83 | self.begin_op(FLAG_A, more); 84 | self.absorb(data); 85 | } 86 | 87 | pub fn prf(&mut self, data: &mut [u8], more: bool) { 88 | self.begin_op(FLAG_I | FLAG_A | FLAG_C, more); 89 | self.squeeze(data); 90 | } 91 | 92 | pub fn key(&mut self, data: &[u8], more: bool) { 93 | self.begin_op(FLAG_A | FLAG_C, more); 94 | self.overwrite(data); 95 | } 96 | } 97 | 98 | impl Strobe128 { 99 | fn run_f(&mut self) { 100 | self.state[self.pos as usize] ^= self.pos_begin; 101 | self.state[(self.pos + 1) as usize] ^= 0x04; 102 | self.state[(STROBE_R + 1) as usize] ^= 0x80; 103 | keccak::f1600(transmute_state(&mut self.state)); 104 | self.pos = 0; 105 | self.pos_begin = 0; 106 | } 107 | 108 | fn absorb(&mut self, data: &[u8]) { 109 | for byte in data { 110 | self.state[self.pos as usize] ^= byte; 111 | self.pos += 1; 112 | if self.pos == STROBE_R { 113 | self.run_f(); 114 | } 115 | } 116 | } 117 | 118 | fn overwrite(&mut self, data: &[u8]) { 119 | for byte in data { 120 | self.state[self.pos as usize] = *byte; 121 | self.pos += 1; 122 | if self.pos == STROBE_R { 123 | self.run_f(); 124 | } 125 | } 126 | } 127 | 128 | fn squeeze(&mut self, data: &mut [u8]) { 129 | for byte in data { 130 | *byte = self.state[self.pos as usize]; 131 | self.state[self.pos as usize] = 0; 132 | self.pos += 1; 133 | if self.pos == STROBE_R { 134 | self.run_f(); 135 | } 136 | } 137 | } 138 | 139 | fn begin_op(&mut self, flags: u8, more: bool) { 140 | // Check if we're continuing an operation 141 | if more { 142 | assert_eq!( 143 | self.cur_flags, flags, 144 | "You tried to continue op {:#b} but changed flags to {:#b}", 145 | self.cur_flags, flags, 146 | ); 147 | return; 148 | } 149 | 150 | // Skip adjusting direction information (we just use AD, PRF) 151 | assert_eq!( 152 | flags & FLAG_T, 153 | 0u8, 154 | "You used the T flag, which this implementation doesn't support" 155 | ); 156 | 157 | let old_begin = self.pos_begin; 158 | self.pos_begin = self.pos + 1; 159 | self.cur_flags = flags; 160 | 161 | self.absorb(&[old_begin, flags]); 162 | 163 | // Force running F if C or K is set 164 | let force_f = 0 != (flags & (FLAG_C | FLAG_K)); 165 | 166 | if force_f && self.pos != 0 { 167 | self.run_f(); 168 | } 169 | } 170 | } 171 | 172 | impl Deref for AlignedKeccakState { 173 | type Target = [u8; 200]; 174 | 175 | fn deref(&self) -> &Self::Target { 176 | &self.0 177 | } 178 | } 179 | 180 | impl DerefMut for AlignedKeccakState { 181 | fn deref_mut(&mut self) -> &mut Self::Target { 182 | &mut self.0 183 | } 184 | } 185 | 186 | #[cfg(test)] 187 | mod tests { 188 | use strobe_rs::{self, SecParam}; 189 | 190 | #[test] 191 | fn test_conformance() { 192 | let mut s1 = super::Strobe128::new(b"Conformance Test Protocol"); 193 | let mut s2 = strobe_rs::Strobe::new(b"Conformance Test Protocol", SecParam::B128); 194 | 195 | // meta-AD(b"msg"); AD(msg) 196 | 197 | let msg = [99u8; 1024]; 198 | 199 | s1.meta_ad(b"ms", false); 200 | s1.meta_ad(b"g", true); 201 | s1.ad(&msg, false); 202 | 203 | s2.meta_ad(b"ms", false); 204 | s2.meta_ad(b"g", true); 205 | s2.ad(&msg, false); 206 | 207 | // meta-AD(b"prf"); PRF() 208 | 209 | let mut prf1 = [0u8; 32]; 210 | s1.meta_ad(b"prf", false); 211 | s1.prf(&mut prf1, false); 212 | 213 | let mut prf2 = [0u8; 32]; 214 | s2.meta_ad(b"prf", false); 215 | s2.prf(&mut prf2, false); 216 | 217 | assert_eq!(prf1, prf2); 218 | 219 | // meta-AD(b"key"); KEY(prf output) 220 | 221 | s1.meta_ad(b"key", false); 222 | s1.key(&prf1, false); 223 | 224 | s2.meta_ad(b"key", false); 225 | s2.key(&prf2, false); 226 | 227 | // meta-AD(b"prf"); PRF() 228 | 229 | let mut prf1 = [0u8; 32]; 230 | s1.meta_ad(b"prf", false); 231 | s1.prf(&mut prf1, false); 232 | 233 | let mut prf2 = [0u8; 32]; 234 | s2.meta_ad(b"prf", false); 235 | s2.prf(&mut prf2, false); 236 | 237 | assert_eq!(prf1, prf2); 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /ed25519-dalek/src/constants.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of ed25519-dalek. 4 | // Copyright (c) 2017-2019 isis lovecruft 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | //! Common constants such as buffer sizes for keypairs and signatures. 11 | 12 | /// The length of a ed25519 `Signature`, in bytes. 13 | pub const SIGNATURE_LENGTH: usize = 64; 14 | 15 | /// The length of a ed25519 `SecretKey`, in bytes. 16 | pub const SECRET_KEY_LENGTH: usize = 32; 17 | 18 | /// The length of an ed25519 `PublicKey`, in bytes. 19 | pub const PUBLIC_KEY_LENGTH: usize = 32; 20 | 21 | /// The length of an ed25519 `Keypair`, in bytes. 22 | pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH; 23 | 24 | /// The length of the "key" portion of an "expanded" ed25519 secret key, in bytes. 25 | const EXPANDED_SECRET_KEY_KEY_LENGTH: usize = 32; 26 | 27 | /// The length of the "nonce" portion of an "expanded" ed25519 secret key, in bytes. 28 | const EXPANDED_SECRET_KEY_NONCE_LENGTH: usize = 32; 29 | 30 | /// The length of an "expanded" ed25519 key, `ExpandedSecretKey`, in bytes. 31 | pub const EXPANDED_SECRET_KEY_LENGTH: usize = 32 | EXPANDED_SECRET_KEY_KEY_LENGTH + EXPANDED_SECRET_KEY_NONCE_LENGTH; 33 | -------------------------------------------------------------------------------- /ed25519-dalek/src/context.rs: -------------------------------------------------------------------------------- 1 | use crate::{InternalError, SignatureError}; 2 | 3 | /// Ed25519 contexts as used by Ed25519ph. 4 | /// 5 | /// Contexts are domain separator strings that can be used to isolate uses of 6 | /// the algorithm between different protocols (which is very hard to reliably do 7 | /// otherwise) and between different uses within the same protocol. 8 | /// 9 | /// To create a context, call either of the following: 10 | /// 11 | /// - [`SigningKey::with_context`](crate::SigningKey::with_context) 12 | /// - [`VerifyingKey::with_context`](crate::VerifyingKey::with_context) 13 | /// 14 | /// For more information, see [RFC8032 § 8.3](https://www.rfc-editor.org/rfc/rfc8032#section-8.3). 15 | /// 16 | /// # Example 17 | /// 18 | #[cfg_attr(all(feature = "digest", feature = "rand_core"), doc = "```")] 19 | #[cfg_attr( 20 | any(not(feature = "digest"), not(feature = "rand_core")), 21 | doc = "```ignore" 22 | )] 23 | /// # fn main() { 24 | /// use ed25519_dalek::{Signature, SigningKey, VerifyingKey, Sha512}; 25 | /// # use curve25519_dalek::digest::Digest; 26 | /// # use rand::rngs::OsRng; 27 | /// # use rand_core::TryRngCore; 28 | /// use ed25519_dalek::{DigestSigner, DigestVerifier}; 29 | /// 30 | /// # let mut csprng = OsRng.unwrap_err(); 31 | /// # let signing_key = SigningKey::generate(&mut csprng); 32 | /// # let verifying_key = signing_key.verifying_key(); 33 | /// let context_str = b"Local Channel 3"; 34 | /// let message = b"Stay tuned for more news at 7"; 35 | /// 36 | /// // Signer 37 | /// let signing_context = signing_key.with_context(context_str).unwrap(); 38 | /// let signature = signing_context.sign_digest(|digest: &mut Sha512| digest.update(message)); 39 | /// 40 | /// // Verifier 41 | /// let verifying_context = verifying_key.with_context(context_str).unwrap(); 42 | /// let verified: bool = verifying_context 43 | /// .verify_digest( 44 | /// |digest: &mut Sha512| { 45 | /// digest.update(message); 46 | /// Ok(()) 47 | /// }, 48 | /// &signature 49 | /// ) 50 | /// .is_ok(); 51 | /// 52 | /// # assert!(verified); 53 | /// # } 54 | /// ``` 55 | #[derive(Clone, Debug)] 56 | pub struct Context<'k, 'v, K> { 57 | /// Key this context is being used with. 58 | key: &'k K, 59 | 60 | /// Context value: a bytestring no longer than 255 octets. 61 | value: &'v [u8], 62 | } 63 | 64 | impl<'k, 'v, K> Context<'k, 'v, K> { 65 | /// Maximum length of the context value in octets. 66 | pub const MAX_LENGTH: usize = 255; 67 | 68 | /// Create a new Ed25519ph context. 69 | pub(crate) fn new(key: &'k K, value: &'v [u8]) -> Result { 70 | if value.len() <= Self::MAX_LENGTH { 71 | Ok(Self { key, value }) 72 | } else { 73 | Err(SignatureError::from(InternalError::PrehashedContextLength)) 74 | } 75 | } 76 | 77 | /// Borrow the key. 78 | pub fn key(&self) -> &'k K { 79 | self.key 80 | } 81 | 82 | /// Borrow the context string value. 83 | pub fn value(&self) -> &'v [u8] { 84 | self.value 85 | } 86 | } 87 | 88 | #[cfg(all(test, feature = "digest"))] 89 | mod test { 90 | #![allow(clippy::unwrap_used)] 91 | 92 | use crate::{Signature, SigningKey, VerifyingKey}; 93 | use curve25519_dalek::digest::Digest; 94 | use rand::{TryRngCore, rngs::OsRng}; 95 | use sha2::Sha512; 96 | use signature::{DigestSigner, DigestVerifier}; 97 | 98 | #[test] 99 | fn context_correctness() { 100 | let mut csprng = OsRng.unwrap_err(); 101 | let signing_key: SigningKey = SigningKey::generate(&mut csprng); 102 | let verifying_key: VerifyingKey = signing_key.verifying_key(); 103 | 104 | let context_str = b"Local Channel 3"; 105 | let message = b"Stay tuned for more news at 7"; 106 | 107 | // Signer 108 | let signing_context = signing_key.with_context(context_str).unwrap(); 109 | let signature: Signature = 110 | signing_context.sign_digest(|digest: &mut Sha512| digest.update(message)); 111 | 112 | // Verifier 113 | let verifying_context = verifying_key.with_context(context_str).unwrap(); 114 | let verified: bool = verifying_context 115 | .verify_digest( 116 | |digest: &mut Sha512| { 117 | digest.update(message); 118 | Ok(()) 119 | }, 120 | &signature, 121 | ) 122 | .is_ok(); 123 | 124 | assert!(verified); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /ed25519-dalek/src/errors.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of ed25519-dalek. 4 | // Copyright (c) 2017-2019 isis lovecruft 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | //! Errors which may occur when parsing keys and/or signatures to or from wire formats. 11 | 12 | // rustc seems to think the typenames in match statements (e.g. in 13 | // Display) should be snake cased, for some reason. 14 | #![allow(non_snake_case)] 15 | 16 | use core::error::Error; 17 | use core::fmt; 18 | use core::fmt::Display; 19 | 20 | /// Internal errors. Most application-level developers will likely not 21 | /// need to pay any attention to these. 22 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 23 | pub(crate) enum InternalError { 24 | PointDecompression, 25 | ScalarFormat, 26 | /// An error in the length of bytes handed to a constructor. 27 | /// 28 | /// To use this, pass a string specifying the `name` of the type which is 29 | /// returning the error, and the `length` in bytes which its constructor 30 | /// expects. 31 | BytesLength { 32 | name: &'static str, 33 | length: usize, 34 | }, 35 | /// The verification equation wasn't satisfied 36 | Verify, 37 | /// Two arrays did not match in size, making the called signature 38 | /// verification method impossible. 39 | #[cfg(feature = "batch")] 40 | ArrayLength { 41 | name_a: &'static str, 42 | length_a: usize, 43 | name_b: &'static str, 44 | length_b: usize, 45 | name_c: &'static str, 46 | length_c: usize, 47 | }, 48 | /// An ed25519ph signature can only take up to 255 octets of context. 49 | #[cfg(feature = "digest")] 50 | PrehashedContextLength, 51 | /// A mismatched (public, secret) key pair. 52 | MismatchedKeypair, 53 | } 54 | 55 | impl Display for InternalError { 56 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 57 | match *self { 58 | InternalError::PointDecompression => write!(f, "Cannot decompress Edwards point"), 59 | InternalError::ScalarFormat => write!(f, "Cannot use scalar with high-bit set"), 60 | InternalError::BytesLength { name: n, length: l } => { 61 | write!(f, "{} must be {} bytes in length", n, l) 62 | } 63 | InternalError::Verify => write!(f, "Verification equation was not satisfied"), 64 | #[cfg(feature = "batch")] 65 | InternalError::ArrayLength { 66 | name_a: na, 67 | length_a: la, 68 | name_b: nb, 69 | length_b: lb, 70 | name_c: nc, 71 | length_c: lc, 72 | } => write!( 73 | f, 74 | "Arrays must be the same length: {} has length {}, 75 | {} has length {}, {} has length {}.", 76 | na, la, nb, lb, nc, lc 77 | ), 78 | #[cfg(feature = "digest")] 79 | InternalError::PrehashedContextLength => write!( 80 | f, 81 | "An ed25519ph signature can only take up to 255 octets of context" 82 | ), 83 | InternalError::MismatchedKeypair => write!(f, "Mismatched Keypair detected"), 84 | } 85 | } 86 | } 87 | 88 | impl Error for InternalError {} 89 | 90 | /// Errors which may occur while processing signatures and keypairs. 91 | /// 92 | /// This error may arise due to: 93 | /// 94 | /// * Being given bytes with a length different to what was expected. 95 | /// 96 | /// * A problem decompressing `r`, a curve point, in the `Signature`, or the 97 | /// curve point for a `PublicKey`. 98 | /// 99 | /// * A problem with the format of `s`, a scalar, in the `Signature`. This 100 | /// is only raised if the high-bit of the scalar was set. (Scalars must 101 | /// only be constructed from 255-bit integers.) 102 | /// 103 | /// * Failure of a signature to satisfy the verification equation. 104 | pub type SignatureError = ed25519::signature::Error; 105 | 106 | impl From for SignatureError { 107 | #[cfg(not(feature = "alloc"))] 108 | fn from(_err: InternalError) -> SignatureError { 109 | SignatureError::new() 110 | } 111 | 112 | #[cfg(feature = "alloc")] 113 | fn from(err: InternalError) -> SignatureError { 114 | SignatureError::from_source(err) 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /ed25519-dalek/src/signature.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of ed25519-dalek. 4 | // Copyright (c) 2017-2019 isis lovecruft 5 | // See LICENSE for licensing information. 6 | // 7 | // Authors: 8 | // - isis agora lovecruft 9 | 10 | //! An ed25519 signature. 11 | 12 | use core::fmt::Debug; 13 | 14 | use curve25519_dalek::edwards::CompressedEdwardsY; 15 | use curve25519_dalek::scalar::Scalar; 16 | 17 | use crate::constants::*; 18 | use crate::errors::*; 19 | 20 | /// An ed25519 signature. 21 | /// 22 | /// # Note 23 | /// 24 | /// These signatures, unlike the ed25519 signature reference implementation, are 25 | /// "detached"—that is, they do **not** include a copy of the message which has 26 | /// been signed. 27 | #[allow(non_snake_case)] 28 | #[derive(Copy, Eq, PartialEq)] 29 | pub(crate) struct InternalSignature { 30 | /// `R` is an `EdwardsPoint`, formed by using an hash function with 31 | /// 512-bits output to produce the digest of: 32 | /// 33 | /// - the nonce half of the `ExpandedSecretKey`, and 34 | /// - the message to be signed. 35 | /// 36 | /// This digest is then interpreted as a `Scalar` and reduced into an 37 | /// element in ℤ/lℤ. The scalar is then multiplied by the distinguished 38 | /// basepoint to produce `R`, and `EdwardsPoint`. 39 | pub(crate) R: CompressedEdwardsY, 40 | 41 | /// `s` is a `Scalar`, formed by using an hash function with 512-bits output 42 | /// to produce the digest of: 43 | /// 44 | /// - the `r` portion of this `Signature`, 45 | /// - the `PublicKey` which should be used to verify this `Signature`, and 46 | /// - the message to be signed. 47 | /// 48 | /// This digest is then interpreted as a `Scalar` and reduced into an 49 | /// element in ℤ/lℤ. 50 | pub(crate) s: Scalar, 51 | } 52 | 53 | impl Clone for InternalSignature { 54 | fn clone(&self) -> Self { 55 | *self 56 | } 57 | } 58 | 59 | impl Debug for InternalSignature { 60 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { 61 | write!(f, "Signature( R: {:?}, s: {:?} )", &self.R, &self.s) 62 | } 63 | } 64 | 65 | /// Ensures that the scalar `s` of a signature is within the bounds [0, 2^253). 66 | /// 67 | /// **Unsafe**: This version of `check_scalar` permits signature malleability. See README. 68 | #[cfg(feature = "legacy_compatibility")] 69 | #[inline(always)] 70 | fn check_scalar(bytes: [u8; 32]) -> Result { 71 | // The highest 3 bits must not be set. No other checking for the 72 | // remaining 2^253 - 2^252 + 27742317777372353535851937790883648493 73 | // potential non-reduced scalars is performed. 74 | // 75 | // This is compatible with ed25519-donna and libsodium when 76 | // `-D ED25519_COMPAT` is NOT specified. 77 | if bytes[31] & 224 != 0 { 78 | return Err(InternalError::ScalarFormat.into()); 79 | } 80 | 81 | // You cannot do arithmetic with scalars construct with Scalar::from_bits. We only use this 82 | // scalar for EdwardsPoint::vartime_double_scalar_mul_basepoint, which is an accepted usecase. 83 | Ok(Scalar::from_bits(bytes)) 84 | } 85 | 86 | /// Ensures that the scalar `s` of a signature is within the bounds [0, ℓ) 87 | #[cfg(not(feature = "legacy_compatibility"))] 88 | #[inline(always)] 89 | fn check_scalar(bytes: [u8; 32]) -> Result { 90 | match Scalar::from_canonical_bytes(bytes).into() { 91 | None => Err(InternalError::ScalarFormat.into()), 92 | Some(x) => Ok(x), 93 | } 94 | } 95 | 96 | impl InternalSignature { 97 | /// Construct a `Signature` from a slice of bytes. 98 | /// 99 | /// # Scalar Malleability Checking 100 | /// 101 | /// As originally specified in the ed25519 paper (cf. the "Malleability" 102 | /// section of the README in this repo), no checks whatsoever were performed 103 | /// for signature malleability. 104 | /// 105 | /// Later, a semi-functional, hacky check was added to most libraries to 106 | /// "ensure" that the scalar portion, `s`, of the signature was reduced `mod 107 | /// \ell`, the order of the basepoint: 108 | /// 109 | /// ```ignore 110 | /// if signature.s[31] & 224 != 0 { 111 | /// return Err(); 112 | /// } 113 | /// ``` 114 | /// 115 | /// This bit-twiddling ensures that the most significant three bits of the 116 | /// scalar are not set: 117 | /// 118 | /// ```python,ignore 119 | /// >>> 0b00010000 & 224 120 | /// 0 121 | /// >>> 0b00100000 & 224 122 | /// 32 123 | /// >>> 0b01000000 & 224 124 | /// 64 125 | /// >>> 0b10000000 & 224 126 | /// 128 127 | /// ``` 128 | /// 129 | /// However, this check is hacky and insufficient to check that the scalar is 130 | /// fully reduced `mod \ell = 2^252 + 27742317777372353535851937790883648493` as 131 | /// it leaves us with a guanteed bound of 253 bits. This means that there are 132 | /// `2^253 - 2^252 + 2774231777737235353585193779088364849311` remaining scalars 133 | /// which could cause malleabilllity. 134 | /// 135 | /// RFC8032 [states](https://tools.ietf.org/html/rfc8032#section-5.1.7): 136 | /// 137 | /// > To verify a signature on a message M using public key A, [...] 138 | /// > first split the signature into two 32-octet halves. Decode the first 139 | /// > half as a point R, and the second half as an integer S, in the range 140 | /// > 0 <= s < L. Decode the public key A as point A'. If any of the 141 | /// > decodings fail (including S being out of range), the signature is 142 | /// > invalid. 143 | /// 144 | /// However, by the time this was standardised, most libraries in use were 145 | /// only checking the most significant three bits. (See also the 146 | /// documentation for [`crate::VerifyingKey::verify_strict`].) 147 | #[inline] 148 | #[allow(non_snake_case)] 149 | pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result { 150 | // TODO: Use bytes.split_array_ref once it’s in MSRV. 151 | let mut R_bytes: [u8; 32] = [0u8; 32]; 152 | let mut s_bytes: [u8; 32] = [0u8; 32]; 153 | R_bytes.copy_from_slice(&bytes[00..32]); 154 | s_bytes.copy_from_slice(&bytes[32..64]); 155 | 156 | Ok(InternalSignature { 157 | R: CompressedEdwardsY(R_bytes), 158 | s: check_scalar(s_bytes)?, 159 | }) 160 | } 161 | } 162 | 163 | impl TryFrom<&ed25519::Signature> for InternalSignature { 164 | type Error = SignatureError; 165 | 166 | fn try_from(sig: &ed25519::Signature) -> Result { 167 | InternalSignature::from_bytes(&sig.to_bytes()) 168 | } 169 | } 170 | 171 | impl From for ed25519::Signature { 172 | fn from(sig: InternalSignature) -> ed25519::Signature { 173 | ed25519::Signature::from_components(*sig.R.as_bytes(), *sig.s.as_bytes()) 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /ed25519-dalek/src/verifying/stream.rs: -------------------------------------------------------------------------------- 1 | use curve25519_dalek::edwards::CompressedEdwardsY; 2 | use sha2::Sha512; 3 | 4 | use crate::verifying::RCompute; 5 | use crate::{InternalError, SignatureError, VerifyingKey, signature::InternalSignature}; 6 | 7 | /// An IUF verifier for ed25519. 8 | /// 9 | /// Created with [`VerifyingKey::verify_stream()`] or [`SigningKey::verify_stream()`]. 10 | /// 11 | /// [`SigningKey::verify_stream()`]: super::SigningKey::verify_stream() 12 | #[allow(non_snake_case)] 13 | pub struct StreamVerifier { 14 | cr: RCompute, 15 | sig_R: CompressedEdwardsY, 16 | } 17 | 18 | impl StreamVerifier { 19 | /// Constructs new stream verifier. 20 | /// 21 | /// Seeds hash state with public key and signature components. 22 | pub(crate) fn new(public_key: VerifyingKey, signature: InternalSignature) -> Self { 23 | Self { 24 | cr: RCompute::new(&public_key, signature, None), 25 | sig_R: signature.R, 26 | } 27 | } 28 | 29 | /// Digest message chunk. 30 | pub fn update(&mut self, chunk: impl AsRef<[u8]>) { 31 | self.cr.update(chunk.as_ref()); 32 | } 33 | 34 | /// Finalize verifier and check against candidate signature. 35 | #[allow(non_snake_case)] 36 | pub fn finalize_and_verify(self) -> Result<(), SignatureError> { 37 | let expected_R = self.cr.finish(); 38 | 39 | if expected_R == self.sig_R { 40 | Ok(()) 41 | } else { 42 | Err(InternalError::Verify.into()) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /ed25519-dalek/tests/examples/pkcs8-v1.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/curve25519-dalek/b76b924080dad8bd07bc26f00bf2d4e4fc5daed1/ed25519-dalek/tests/examples/pkcs8-v1.der -------------------------------------------------------------------------------- /ed25519-dalek/tests/examples/pkcs8-v2.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/curve25519-dalek/b76b924080dad8bd07bc26f00bf2d4e4fc5daed1/ed25519-dalek/tests/examples/pkcs8-v2.der -------------------------------------------------------------------------------- /ed25519-dalek/tests/examples/pubkey.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/curve25519-dalek/b76b924080dad8bd07bc26f00bf2d4e4fc5daed1/ed25519-dalek/tests/examples/pubkey.der -------------------------------------------------------------------------------- /ed25519-dalek/tests/pkcs8.rs: -------------------------------------------------------------------------------- 1 | //! PKCS#8 private key and SPKI public key tests. 2 | //! 3 | //! These are standard formats for storing public and private keys, defined in 4 | //! RFC5958 (PKCS#8) and RFC5280 (SPKI). 5 | 6 | #![cfg(feature = "pkcs8")] 7 | use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey}; 8 | use ed25519_dalek::{SigningKey, VerifyingKey}; 9 | use hex_literal::hex; 10 | 11 | #[cfg(feature = "alloc")] 12 | use ed25519_dalek::pkcs8::{EncodePrivateKey, EncodePublicKey}; 13 | 14 | #[cfg(all(feature = "alloc", feature = "pkcs8"))] 15 | use ed25519_dalek::pkcs8::spki::DynSignatureAlgorithmIdentifier; 16 | 17 | /// Ed25519 PKCS#8 v1 private key encoded as ASN.1 DER. 18 | const PKCS8_V1_DER: &[u8] = include_bytes!("examples/pkcs8-v1.der"); 19 | 20 | /// Ed25519 PKCS#8 v2 private key + public key encoded as ASN.1 DER. 21 | const PKCS8_V2_DER: &[u8] = include_bytes!("examples/pkcs8-v2.der"); 22 | 23 | /// Ed25519 SubjectVerifyingKeyInfo encoded as ASN.1 DER. 24 | const PUBLIC_KEY_DER: &[u8] = include_bytes!("examples/pubkey.der"); 25 | 26 | /// Secret key bytes. 27 | /// 28 | /// Extracted with: 29 | /// $ openssl asn1parse -inform der -in tests/examples/pkcs8-v1.der 30 | const SK_BYTES: [u8; 32] = hex!("D4EE72DBF913584AD5B6D8F1F769F8AD3AFE7C28CBF1D4FBE097A88F44755842"); 31 | 32 | /// Public key bytes. 33 | const PK_BYTES: [u8; 32] = hex!("19BF44096984CDFE8541BAC167DC3B96C85086AA30B6B6CB0C5C38AD703166E1"); 34 | 35 | #[test] 36 | fn decode_pkcs8_v1() { 37 | let keypair = SigningKey::from_pkcs8_der(PKCS8_V1_DER).unwrap(); 38 | assert_eq!(SK_BYTES, keypair.to_bytes()); 39 | assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes()); 40 | } 41 | 42 | #[test] 43 | fn decode_pkcs8_v2() { 44 | let keypair = SigningKey::from_pkcs8_der(PKCS8_V2_DER).unwrap(); 45 | assert_eq!(SK_BYTES, keypair.to_bytes()); 46 | assert_eq!(PK_BYTES, keypair.verifying_key().to_bytes()); 47 | } 48 | 49 | #[test] 50 | fn decode_verifying_key() { 51 | let verifying_key = VerifyingKey::from_public_key_der(PUBLIC_KEY_DER).unwrap(); 52 | assert_eq!(PK_BYTES, verifying_key.to_bytes()); 53 | } 54 | 55 | #[test] 56 | #[cfg(feature = "alloc")] 57 | fn encode_pkcs8() { 58 | let keypair = SigningKey::from_bytes(&SK_BYTES); 59 | let pkcs8_key = keypair.to_pkcs8_der().unwrap(); 60 | 61 | let keypair2 = SigningKey::from_pkcs8_der(pkcs8_key.as_bytes()).unwrap(); 62 | assert_eq!(keypair.to_bytes(), keypair2.to_bytes()); 63 | } 64 | 65 | #[test] 66 | #[cfg(feature = "alloc")] 67 | fn encode_verifying_key() { 68 | let verifying_key = VerifyingKey::from_bytes(&PK_BYTES).unwrap(); 69 | let verifying_key_der = verifying_key.to_public_key_der().unwrap(); 70 | 71 | let verifying_key2 = VerifyingKey::from_public_key_der(verifying_key_der.as_bytes()).unwrap(); 72 | assert_eq!(verifying_key, verifying_key2); 73 | } 74 | 75 | #[test] 76 | #[cfg(feature = "alloc")] 77 | fn get_algo_identifier() { 78 | let verifying_key = VerifyingKey::from_public_key_der(PUBLIC_KEY_DER).unwrap(); 79 | let identifier = verifying_key.signature_algorithm_identifier().unwrap(); 80 | assert!(identifier.parameters.is_none()); // According to rfc8410 this must be None 81 | assert_eq!(identifier.oid, ed25519::pkcs8::ALGORITHM_OID); 82 | 83 | let signing_key = SigningKey::from_bytes(&SK_BYTES); 84 | let identifier = signing_key.signature_algorithm_identifier().unwrap(); 85 | assert!(identifier.parameters.is_none()); // According to rfc8410 this must be None 86 | assert_eq!(identifier.oid, ed25519::pkcs8::ALGORITHM_OID); 87 | } 88 | -------------------------------------------------------------------------------- /ed25519-dalek/tests/x25519.rs: -------------------------------------------------------------------------------- 1 | //! Tests for converting Ed25519 keys into X25519 (Montgomery form) keys. 2 | 3 | use curve25519_dalek::scalar::{Scalar, clamp_integer}; 4 | use ed25519_dalek::SigningKey; 5 | use hex_literal::hex; 6 | use sha2::{Digest, Sha512}; 7 | use x25519_dalek::{PublicKey as XPublicKey, StaticSecret as XStaticSecret}; 8 | 9 | /// Tests that X25519 Diffie-Hellman works when using keys converted from Ed25519. 10 | // TODO: generate test vectors using another implementation of Ed25519->X25519 11 | #[test] 12 | fn ed25519_to_x25519_dh() { 13 | // Keys from RFC8032 test vectors (from section 7.1) 14 | let ed_secret_key_a = hex!("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60"); 15 | let ed_secret_key_b = hex!("4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb"); 16 | 17 | let ed_signing_key_a = SigningKey::from_bytes(&ed_secret_key_a); 18 | let ed_signing_key_b = SigningKey::from_bytes(&ed_secret_key_b); 19 | 20 | // Create an x25519 static secret from the ed25519 signing key 21 | let scalar_bytes_a = ed_signing_key_a.to_scalar_bytes(); 22 | let scalar_bytes_b = ed_signing_key_b.to_scalar_bytes(); 23 | let x_static_secret_a = XStaticSecret::from(scalar_bytes_a); 24 | let x_static_secret_b = XStaticSecret::from(scalar_bytes_b); 25 | 26 | // Compute the secret scalars too 27 | let scalar_a = ed_signing_key_a.to_scalar(); 28 | let scalar_b = ed_signing_key_b.to_scalar(); 29 | 30 | // Compare the scalar bytes to the first 32 bytes of SHA-512(secret_key). We have to clamp and 31 | // reduce the SHA-512 output because that's what the spec does before using the scalars for 32 | // anything. 33 | assert_eq!(scalar_bytes_a, &Sha512::digest(ed_secret_key_a)[..32]); 34 | assert_eq!(scalar_bytes_b, &Sha512::digest(ed_secret_key_b)[..32]); 35 | 36 | // Compare the scalar with the clamped and reduced scalar bytes 37 | assert_eq!( 38 | scalar_a, 39 | Scalar::from_bytes_mod_order(clamp_integer(scalar_bytes_a)) 40 | ); 41 | assert_eq!( 42 | scalar_b, 43 | Scalar::from_bytes_mod_order(clamp_integer(scalar_bytes_b)) 44 | ); 45 | 46 | let x_public_key_a = XPublicKey::from(&x_static_secret_a); 47 | let x_public_key_b = XPublicKey::from(&x_static_secret_b); 48 | assert_eq!( 49 | x_public_key_a.to_bytes(), 50 | hex!("d85e07ec22b0ad881537c2f44d662d1a143cf830c57aca4305d85c7a90f6b62e") 51 | ); 52 | assert_eq!( 53 | x_public_key_b.to_bytes(), 54 | hex!("25c704c594b88afc00a76b69d1ed2b984d7e22550f3ed0802d04fbcd07d38d47") 55 | ); 56 | 57 | // Test the claim made in the comments of SigningKey::to_scalar_bytes, i.e., that the resulting 58 | // scalar is a valid private key for the x25519 pubkey represented by 59 | // `sk.verifying_key().to_montgomery()` 60 | assert_eq!( 61 | ed_signing_key_a.verifying_key().to_montgomery().as_bytes(), 62 | x_public_key_a.as_bytes() 63 | ); 64 | assert_eq!( 65 | ed_signing_key_b.verifying_key().to_montgomery().as_bytes(), 66 | x_public_key_b.as_bytes() 67 | ); 68 | 69 | // Check that Diffie-Hellman works 70 | let expected_shared_secret = 71 | hex!("5166f24a6918368e2af831a4affadd97af0ac326bdf143596c045967cc00230e"); 72 | assert_eq!( 73 | x_static_secret_a.diffie_hellman(&x_public_key_b).to_bytes(), 74 | expected_shared_secret, 75 | ); 76 | assert_eq!( 77 | x_static_secret_b.diffie_hellman(&x_public_key_a).to_bytes(), 78 | expected_shared_secret, 79 | ); 80 | } 81 | -------------------------------------------------------------------------------- /x25519-dalek/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | 4 | .cargo 5 | 6 | *~ 7 | \#* 8 | .\#* 9 | *.swp 10 | *.orig 11 | *.bak 12 | 13 | *.s 14 | -------------------------------------------------------------------------------- /x25519-dalek/.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | rust: 4 | - nightly 5 | 6 | env: 7 | - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='default' 8 | - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly' 9 | - TEST_COMMAND=bench EXTRA_FLAGS='' FEATURES='default' 10 | - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend nightly' 11 | - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u64_backend nightly' 12 | 13 | matrix: 14 | include: 15 | - rust: stable 16 | env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u64_backend' 17 | - rust: beta 18 | env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u64_backend' 19 | 20 | script: 21 | - cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS 22 | 23 | notifications: 24 | slack: 25 | rooms: 26 | - dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots 27 | -------------------------------------------------------------------------------- /x25519-dalek/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Entries are listed in reverse chronological order. 4 | 5 | # 3.x Series 6 | 7 | ## 3.0.0-pre.0 8 | 9 | * Update edition to 2024 10 | * Update the MSRV from 1.60 to 1.85 11 | * Update `rand_core` dep 12 | * Remove `Zeroize` impl for `x25519::{EphemeralSecret, ReusableSecret, SharedSecret, StaticSecret}` to prevent misuse. These are now only zeroized on drop. 13 | * Remove deprecated functions `{Ephemeral,Reusable,Static}Secret::new()` 14 | 15 | # 2.x Series 16 | 17 | * Note: All `x255919-dalek` 2.x releases are in sync with the underlying `curve25519-dalek` 4.x releases. 18 | 19 | ## 2.0.1 20 | 21 | * Fix nightly SIMD build 22 | 23 | ## 2.0.0-rc.3 24 | 25 | * `StaticSecret` serialization and `to_bytes()` no longer returns clamped integers. Clamping is still always done during scalar-point multiplication. 26 | * Update underlying `curve25519_dalek` library to `4.0.0-rc.3`. Notable changes: 27 | * [curve25519-dalek backend] now by default auto selects `simd` backend over `serial` where supported. 28 | 29 | 30 | ## 2.0.0-rc.2 31 | 32 | * Update MSRV to 1.60. 33 | * Update edition to 2021 34 | * Add `.as_bytes()` and `AsRef<[u8]>` for `Shared/StaticSecret` 35 | * Add `getrandom` feature to provide `random_from_rng` constructors 36 | * Make `StaticSecrets` optional via feature `static_secrets` 37 | * Update underlying `curve25519_dalek` library to `4.0.0-rc.2`. Notable changes: 38 | * [curve25519-dalek backend] additive features have been removed in favor of cfg based selection. 39 | * [curve25519-dalek backend] now by default auto selects the appropriate word size over the previous default `32`. 40 | 41 | ## 2.0.0-pre.1 42 | 43 | * Loosen restriction on zeroize dependency version from =1.3 to 1. 44 | * Update MSRV to 1.51. 45 | 46 | ## 2.0.0-pre.0 47 | 48 | * Update `rand_core` dependency to `0.6`. 49 | 50 | # 1.x Series 51 | 52 | ## 1.2 53 | 54 | * Add module documentation for using the bytes-oriented `x25519()` API. 55 | * Add implementation of `zeroize::Zeroize` for `PublicKey`. 56 | * Move unittests to a separate directory. 57 | * Add cargo feature flags `"fiat_u32_backend"` and `"fiat_u64_backend"` for 58 | activating the Fiat crypto field element implementations. 59 | * Fix issue with removed `feature(external_doc)` on nightly compilers. 60 | * Pin `zeroize` to version 1.3 to support a wider range of MSRVs. 61 | * Add CI via Github actions. 62 | * Fix breakage in the serde unittests. 63 | * MSRV is now 1.41 for production and 1.48 for development. 64 | * Add an optional check to `SharedSecret` for contibutory behaviour. 65 | * Add implementation of `ReusableSecret` keys which are non-ephemeral, but which 66 | cannot be serialised to discourage long-term use. 67 | 68 | ## 1.1.1 69 | 70 | * Fix a typo in the README. 71 | 72 | ## 1.1.0 73 | 74 | * Add impls of `PartialEq`, `Eq`, and `Hash` for `PublicKey` (by @jack-michaud) 75 | 76 | ## 1.0.1 77 | 78 | * Update underlying `curve25519_dalek` library to `3.0`. 79 | 80 | ## 1.0.0 81 | 82 | * Widen generic bound on `EphemeralSecret::new` and `StaticSecret::new` to 83 | allow owned as well as borrowed RNGs. 84 | * Add `PublicKey::to_bytes` and `SharedSecret::to_bytes`, returning owned byte 85 | arrays, complementing the existing `as_bytes` methods returning references. 86 | * Remove mention of deprecated `rand_os` crate from examples. 87 | * Clarify `EphemeralSecret`/`StaticSecret` distinction in documentation. 88 | 89 | # Pre-1.0.0 90 | 91 | ## 0.6.0 92 | 93 | * Updates `rand_core` version to `0.5`. 94 | * Adds `serde` support. 95 | * Replaces `clear_on_drop` with `zeroize`. 96 | * Use Rust 2018. 97 | 98 | ## 0.5.2 99 | 100 | * Implement `Clone` for `StaticSecret`. 101 | 102 | ## 0.5.1 103 | 104 | * Implement `Copy, Clone, Debug` for `PublicKey`. 105 | * Remove doctests. 106 | 107 | ## 0.5.0 108 | 109 | * Adds support for static and ephemeral keys. 110 | 111 | [curve25519-dalek backend]: https://github.com/dalek-cryptography/curve25519-dalek/#backends 112 | -------------------------------------------------------------------------------- /x25519-dalek/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "x25519-dalek" 3 | edition = "2024" 4 | # Before changing this: 5 | # - update version in README.md 6 | # - update html_root_url 7 | # - update CHANGELOG 8 | # - if any changes were made to README.md, mirror them in src/lib.rs docs 9 | version = "3.0.0-pre.1" 10 | authors = [ 11 | "Isis Lovecruft ", 12 | "DebugSteven ", 13 | "Henry de Valence ", 14 | ] 15 | readme = "README.md" 16 | license = "BSD-3-Clause" 17 | repository = "https://github.com/dalek-cryptography/curve25519-dalek/tree/main/x25519-dalek" 18 | homepage = "https://github.com/dalek-cryptography/curve25519-dalek" 19 | documentation = "https://docs.rs/x25519-dalek" 20 | categories = ["cryptography", "no-std"] 21 | keywords = [ 22 | "cryptography", 23 | "curve25519", 24 | "key-exchange", 25 | "x25519", 26 | "diffie-hellman", 27 | ] 28 | description = "X25519 elliptic curve Diffie-Hellman key exchange in pure-Rust, using curve25519-dalek." 29 | exclude = [".gitignore", ".travis.yml", "CONTRIBUTING.md"] 30 | rust-version = "1.85" 31 | 32 | [badges] 33 | travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master" } 34 | 35 | [package.metadata.docs.rs] 36 | rustdoc-args = [ 37 | "--html-in-header", 38 | "docs/assets/rustdoc-include-katex-header.html", 39 | "--cfg", 40 | "docsrs", 41 | ] 42 | features = ["os_rng", "reusable_secrets", "serde", "static_secrets"] 43 | 44 | [dependencies] 45 | curve25519-dalek = { version = "=5.0.0-pre.1", default-features = false } 46 | rand_core = { version = "0.9", default-features = false } 47 | serde = { version = "1", default-features = false, optional = true, features = [ 48 | "derive", 49 | ] } 50 | zeroize = { version = "1", default-features = false, optional = true } 51 | 52 | [dev-dependencies] 53 | bincode = "1" 54 | criterion = "0.5" 55 | rand_core = { version = "0.9", default-features = false, features = ["os_rng"] } 56 | 57 | [[bench]] 58 | name = "x25519" 59 | harness = false 60 | 61 | [features] 62 | default = ["alloc", "precomputed-tables", "zeroize"] 63 | os_rng = ["rand_core/os_rng"] 64 | zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] 65 | serde = ["dep:serde", "curve25519-dalek/serde"] 66 | alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] 67 | precomputed-tables = ["curve25519-dalek/precomputed-tables"] 68 | reusable_secrets = [] 69 | static_secrets = [] 70 | -------------------------------------------------------------------------------- /x25519-dalek/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017-2021 isis agora lovecruft. All rights reserved. 2 | Copyright (c) 2019-2021 DebugSteven. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are 6 | met: 7 | 8 | 1. Redistributions of source code must retain the above copyright 9 | notice, this list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 20 | IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 22 | PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 25 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 26 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 27 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 28 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 29 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /x25519-dalek/README.md: -------------------------------------------------------------------------------- 1 | # x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![CI](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/x25519-dalek.yml/badge.svg?branch=main)](https://github.com/dalek-cryptography/curve25519-dalek/actions/workflows/x25519-dalek.yml) 2 | 3 | A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, 4 | with curve operations provided by 5 | [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek). 6 | 7 | This crate provides two levels of API: a bare byte-oriented `x25519` 8 | function which matches the function specified in [RFC7748][rfc7748], as 9 | well as a higher-level Rust API for static and ephemeral Diffie-Hellman. 10 | 11 | ## Examples 12 | 13 | 14 | 17 | 18 | 19 | Alice and Bob are two adorable kittens who have lost their mittens, and they 20 | wish to be able to send secret messages to each other to coordinate finding 21 | them, otherwise—if their caretaker cat finds out—they will surely be called 22 | naughty kittens and be given no pie! 23 | 24 | But the two kittens are quite clever. Even though their paws are still too big 25 | and the rest of them is 90% fuzziness, these clever kittens have been studying 26 | up on modern public key cryptography and have learned a nifty trick called 27 | *elliptic curve Diffie-Hellman key exchange*. With the right incantations, the 28 | kittens will be able to secretly organise to find their mittens, and then spend 29 | the rest of the afternoon nomming some yummy pie! 30 | 31 | First, Alice uses `EphemeralSecret::random()` and then 32 | `PublicKey::from()` to produce her secret and public keys: 33 | 34 | ```ignore 35 | use x25519_dalek::{EphemeralSecret, PublicKey}; 36 | 37 | let alice_secret = EphemeralSecret::random(); 38 | let alice_public = PublicKey::from(&alice_secret); 39 | ``` 40 | 41 | Bob does the same: 42 | 43 | ```ignore 44 | # use x25519_dalek::{EphemeralSecret, PublicKey}; 45 | let bob_secret = EphemeralSecret::random(); 46 | let bob_public = PublicKey::from(&bob_secret); 47 | ``` 48 | 49 | Alice meows across the room, telling `alice_public` to Bob, and Bob 50 | loudly meows `bob_public` back to Alice. Alice now computes her 51 | shared secret with Bob by doing: 52 | 53 | ```rust 54 | # use rand_core::{OsRng, TryRngCore}; 55 | # use x25519_dalek::{EphemeralSecret, PublicKey}; 56 | # let mut rng = OsRng.unwrap_err(); 57 | # let alice_secret = EphemeralSecret::random_from_rng(&mut rng); 58 | # let alice_public = PublicKey::from(&alice_secret); 59 | # let bob_secret = EphemeralSecret::random_from_rng(&mut rng); 60 | # let bob_public = PublicKey::from(&bob_secret); 61 | let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); 62 | ``` 63 | 64 | Similarly, Bob computes a shared secret by doing: 65 | 66 | ```rust 67 | # use rand_core::{OsRng, TryRngCore}; 68 | # use x25519_dalek::{EphemeralSecret, PublicKey}; 69 | # let mut rng = OsRng.unwrap_err(); 70 | # let alice_secret = EphemeralSecret::random_from_rng(&mut rng); 71 | # let alice_public = PublicKey::from(&alice_secret); 72 | # let bob_secret = EphemeralSecret::random_from_rng(&mut rng); 73 | # let bob_public = PublicKey::from(&bob_secret); 74 | let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); 75 | ``` 76 | 77 | These secrets are the same: 78 | 79 | ```rust 80 | # use rand_core::{OsRng, TryRngCore}; 81 | # use x25519_dalek::{EphemeralSecret, PublicKey}; 82 | # let mut rng = OsRng.unwrap_err(); 83 | # let alice_secret = EphemeralSecret::random_from_rng(&mut rng); 84 | # let alice_public = PublicKey::from(&alice_secret); 85 | # let bob_secret = EphemeralSecret::random_from_rng(&mut rng); 86 | # let bob_public = PublicKey::from(&bob_secret); 87 | # let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); 88 | # let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); 89 | assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); 90 | ``` 91 | 92 | Voilà! Alice and Bob can now use their shared secret to encrypt their 93 | meows, for example, by using it to generate a key and nonce for an 94 | authenticated-encryption cipher. 95 | 96 | This example used the ephemeral DH API, which ensures that secret keys 97 | cannot be reused; Alice and Bob could instead use the static DH API 98 | and load a long-term secret key. 99 | 100 | # Installation 101 | 102 | To install, add the following to your project's `Cargo.toml`: 103 | 104 | ```toml 105 | [dependencies] 106 | x25519-dalek = "3.0.0-pre.0" 107 | ``` 108 | 109 | # MSRV 110 | 111 | Current MSRV is 1.85. 112 | 113 | # Documentation 114 | 115 | Documentation is available [here](https://docs.rs/x25519-dalek). 116 | 117 | # Performance and backend selection 118 | 119 | Performance is a secondary goal behind correctness, safety, and clarity, but we aim to be competitive with other implementations. To this end, we allow users to choose their _backend_, i.e., the underlying implementation of elliptic curve and scalar arithmetic. Different backends have different use cases. For example, if you demand formally verified code, you want to use the `fiat` backend (as it was generated from [Fiat Crypto][fiat]). 120 | 121 | Further instructions and details regarding backends can be found in the [curve25519-dalek docs](https://github.com/dalek-cryptography/curve25519-dalek#backends). 122 | 123 | # Note 124 | 125 | This code matches the [RFC7748][rfc7748] test vectors. 126 | The elliptic curve 127 | operations are provided by `curve25519-dalek`, which makes a best-effort 128 | attempt to prevent software side-channels. 129 | 130 | "Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) 131 | copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) 132 | 133 | [rfc7748]: https://tools.ietf.org/html/rfc7748 134 | 135 | # See also 136 | 137 | - [crypto_box]: pure Rust public-key authenticated encryption compatible with 138 | the NaCl family of encryption libraries (libsodium, TweetNaCl) which uses 139 | `x25519-dalek` for key agreement 140 | 141 | [fiat]: https://github.com/mit-plv/fiat-crypto 142 | [crypto_box]: https://github.com/RustCrypto/nacl-compat/tree/master/crypto_box 143 | -------------------------------------------------------------------------------- /x25519-dalek/benches/x25519.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of x25519-dalek. 4 | // Copyright (c) 2017-2019 isis agora lovecruft 5 | // Copyright (c) 2019 DebugSteven 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - DebugSteven 11 | 12 | //! Benchmark the Diffie-Hellman operation. 13 | 14 | use criterion::{Criterion, criterion_group, criterion_main}; 15 | 16 | use rand_core::{OsRng, TryRngCore}; 17 | 18 | use x25519_dalek::EphemeralSecret; 19 | use x25519_dalek::PublicKey; 20 | 21 | fn bench_diffie_hellman(c: &mut Criterion) { 22 | let bob_secret = EphemeralSecret::random_from_rng(&mut OsRng.unwrap_err()); 23 | let bob_public = PublicKey::from(&bob_secret); 24 | 25 | c.bench_function("diffie_hellman", move |b| { 26 | b.iter_with_setup( 27 | || EphemeralSecret::random_from_rng(&mut OsRng.unwrap_err()), 28 | |alice_secret| alice_secret.diffie_hellman(&bob_public), 29 | ) 30 | }); 31 | } 32 | 33 | criterion_group! { 34 | name = x25519_benches; 35 | config = Criterion::default(); 36 | targets = 37 | bench_diffie_hellman, 38 | } 39 | criterion_main! { 40 | x25519_benches, 41 | } 42 | -------------------------------------------------------------------------------- /x25519-dalek/docs/assets/rustdoc-include-katex-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 13 | -------------------------------------------------------------------------------- /x25519-dalek/res/bubblesort-zines-secret-messages-cover.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/curve25519-dalek/b76b924080dad8bd07bc26f00bf2d4e4fc5daed1/x25519-dalek/res/bubblesort-zines-secret-messages-cover.jpeg -------------------------------------------------------------------------------- /x25519-dalek/src/lib.rs: -------------------------------------------------------------------------------- 1 | // -*- mode: rust; -*- 2 | // 3 | // This file is part of x25519-dalek. 4 | // Copyright (c) 2017-2021 isis lovecruft 5 | // Copyright (c) 2019-2021 DebugSteven 6 | // See LICENSE for licensing information. 7 | // 8 | // Authors: 9 | // - isis agora lovecruft 10 | // - DebugSteven 11 | 12 | // Refuse to compile if documentation is missing, but only on nightly. 13 | // 14 | // This means that missing docs will still fail CI, but means we can use 15 | // README.md as the crate documentation. 16 | 17 | #![no_std] 18 | #![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))] 19 | #![cfg_attr(docsrs, doc(cfg_hide(docsrs)))] 20 | #![deny(missing_docs)] 21 | #![doc( 22 | html_logo_url = "https://cdn.jsdelivr.net/gh/dalek-cryptography/curve25519-dalek/docs/assets/dalek-logo-clear.png" 23 | )] 24 | #![doc = include_str!("../README.md")] 25 | 26 | //------------------------------------------------------------------------ 27 | // x25519-dalek public API 28 | //------------------------------------------------------------------------ 29 | 30 | mod x25519; 31 | 32 | pub use crate::x25519::*; 33 | -------------------------------------------------------------------------------- /x25519-dalek/tests/x25519_tests.rs: -------------------------------------------------------------------------------- 1 | use curve25519_dalek::edwards::EdwardsPoint; 2 | 3 | use x25519_dalek::*; 4 | 5 | #[test] 6 | fn byte_basepoint_matches_edwards_scalar_mul() { 7 | let mut scalar_bytes = [0x37; 32]; 8 | 9 | for i in 0..32 { 10 | scalar_bytes[i] += 2; 11 | 12 | let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES); 13 | let expected = EdwardsPoint::mul_base_clamped(scalar_bytes) 14 | .to_montgomery() 15 | .to_bytes(); 16 | 17 | assert_eq!(result, expected); 18 | } 19 | } 20 | 21 | #[test] 22 | #[cfg(feature = "serde")] 23 | fn serde_bincode_public_key_roundtrip() { 24 | use bincode; 25 | 26 | let public_key = PublicKey::from(X25519_BASEPOINT_BYTES); 27 | 28 | let encoded = bincode::serialize(&public_key).unwrap(); 29 | let decoded: PublicKey = bincode::deserialize(&encoded).unwrap(); 30 | 31 | assert_eq!(encoded.len(), 32); 32 | assert_eq!(decoded.as_bytes(), public_key.as_bytes()); 33 | } 34 | 35 | #[test] 36 | #[cfg(feature = "serde")] 37 | fn serde_bincode_public_key_matches_from_bytes() { 38 | use bincode; 39 | 40 | let expected = PublicKey::from(X25519_BASEPOINT_BYTES); 41 | let decoded: PublicKey = bincode::deserialize(&X25519_BASEPOINT_BYTES).unwrap(); 42 | 43 | assert_eq!(decoded.as_bytes(), expected.as_bytes()); 44 | } 45 | 46 | #[test] 47 | #[cfg(feature = "serde")] 48 | fn serde_bincode_static_secret_roundtrip() { 49 | use bincode; 50 | 51 | let static_secret = StaticSecret::from([0x24; 32]); 52 | let encoded = bincode::serialize(&static_secret).unwrap(); 53 | let decoded: StaticSecret = bincode::deserialize(&encoded).unwrap(); 54 | 55 | assert_eq!(encoded.len(), 32); 56 | assert_eq!(decoded.to_bytes(), static_secret.to_bytes()); 57 | } 58 | 59 | #[test] 60 | #[cfg(feature = "serde")] 61 | fn serde_bincode_static_secret_matches_from_bytes() { 62 | use bincode; 63 | 64 | let expected = StaticSecret::from([0x24; 32]); 65 | let decoded: StaticSecret = bincode::deserialize(&[0x24; 32]).unwrap(); 66 | 67 | assert_eq!(decoded.to_bytes(), expected.to_bytes()); 68 | } 69 | 70 | fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], input_point: [u8; 32], expected: [u8; 32]) { 71 | let result = x25519(input_scalar, input_point); 72 | 73 | assert_eq!(result, expected); 74 | } 75 | 76 | #[test] 77 | fn rfc7748_ladder_test1_vectorset1() { 78 | let input_scalar: [u8; 32] = [ 79 | 0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e, 80 | 0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44, 81 | 0x9a, 0xc4, 82 | ]; 83 | let input_point: [u8; 32] = [ 84 | 0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f, 85 | 0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab, 86 | 0x1c, 0x4c, 87 | ]; 88 | let expected: [u8; 32] = [ 89 | 0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08, 90 | 0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2, 91 | 0x85, 0x52, 92 | ]; 93 | 94 | do_rfc7748_ladder_test1(input_scalar, input_point, expected); 95 | } 96 | 97 | #[test] 98 | fn rfc7748_ladder_test1_vectorset2() { 99 | let input_scalar: [u8; 32] = [ 100 | 0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a, 101 | 0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18, 102 | 0xba, 0x0d, 103 | ]; 104 | let input_point: [u8; 32] = [ 105 | 0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae, 106 | 0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15, 107 | 0xa4, 0x93, 108 | ]; 109 | let expected: [u8; 32] = [ 110 | 0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73, 111 | 0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac, 112 | 0x79, 0x57, 113 | ]; 114 | 115 | do_rfc7748_ladder_test1(input_scalar, input_point, expected); 116 | } 117 | 118 | #[test] 119 | #[ignore] // Run only if you want to burn a lot of CPU doing 1,000,000 DH operations 120 | fn rfc7748_ladder_test2() { 121 | use curve25519_dalek::constants::X25519_BASEPOINT; 122 | 123 | let mut k: [u8; 32] = X25519_BASEPOINT.0; 124 | let mut u: [u8; 32] = X25519_BASEPOINT.0; 125 | let mut result: [u8; 32]; 126 | 127 | macro_rules! do_iterations { 128 | ($n:expr) => { 129 | for _ in 0..$n { 130 | result = x25519(k, u); 131 | // OBVIOUS THING THAT I'M GOING TO NOTE ANYWAY BECAUSE I'VE 132 | // SEEN PEOPLE DO THIS WITH GOLANG'S STDLIB AND YOU SURE AS 133 | // HELL SHOULDN'T DO HORRIBLY STUPID THINGS LIKE THIS WITH 134 | // MY LIBRARY: 135 | // 136 | // NEVER EVER TREAT SCALARS AS POINTS AND/OR VICE VERSA. 137 | // 138 | // ↓↓ DON'T DO THIS ↓↓ 139 | u = k.clone(); 140 | k = result; 141 | } 142 | }; 143 | } 144 | 145 | // After one iteration: 146 | // 422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079 147 | // After 1,000 iterations: 148 | // 684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51 149 | // After 1,000,000 iterations: 150 | // 7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424 151 | 152 | do_iterations!(1); 153 | assert_eq!( 154 | k, 155 | [ 156 | 0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7, 157 | 0x27, 0x9f, 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, 0x3c, 0x60, 0xe8, 0x03, 158 | 0x11, 0xae, 0x30, 0x79, 159 | ] 160 | ); 161 | do_iterations!(999); 162 | assert_eq!( 163 | k, 164 | [ 165 | 0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f, 166 | 0x4d, 0x3c, 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, 0x5f, 0x2e, 0xb9, 0x4d, 167 | 0x99, 0x53, 0x2c, 0x51, 168 | ] 169 | ); 170 | do_iterations!(999_000); 171 | assert_eq!( 172 | k, 173 | [ 174 | 0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57, 175 | 0x5e, 0x6f, 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, 0x5f, 0x4d, 0xd2, 0xd2, 176 | 0x4f, 0x66, 0x54, 0x24, 177 | ] 178 | ); 179 | } 180 | 181 | mod rand_core { 182 | 183 | use super::*; 184 | use ::rand_core::{OsRng, TryRngCore}; 185 | 186 | #[test] 187 | fn ephemeral_from_rng() { 188 | EphemeralSecret::random_from_rng(&mut OsRng.unwrap_err()); 189 | } 190 | 191 | #[test] 192 | #[cfg(feature = "reusable_secrets")] 193 | fn reusable_from_rng() { 194 | ReusableSecret::random_from_rng(&mut OsRng.unwrap_err()); 195 | } 196 | 197 | #[test] 198 | #[cfg(feature = "static_secrets")] 199 | fn static_from_rng() { 200 | StaticSecret::random_from_rng(&mut OsRng.unwrap_err()); 201 | } 202 | } 203 | 204 | #[cfg(feature = "os_rng")] 205 | mod os_rng { 206 | 207 | use super::*; 208 | 209 | #[test] 210 | fn ephemeral_random() { 211 | EphemeralSecret::random(); 212 | } 213 | 214 | #[test] 215 | #[cfg(feature = "reusable_secrets")] 216 | fn reusable_random() { 217 | ReusableSecret::random(); 218 | } 219 | 220 | #[test] 221 | #[cfg(feature = "static_secrets")] 222 | fn static_random() { 223 | StaticSecret::random(); 224 | } 225 | } 226 | --------------------------------------------------------------------------------