├── .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 |
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://crates.io/crates/curve25519-dalek) | [](https://docs.rs/curve25519-dalek) | [](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://crates.io/crates/ed25519-dalek) | [](https://docs.rs/ed25519-dalek) | [](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://crates.io/crates/x25519-dalek) | [](https://docs.rs/x25519-dalek) | [](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://crates.io/crates/x25519-dalek) [](https://docs.rs/x25519-dalek) [](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 |
--------------------------------------------------------------------------------