├── .github └── workflows │ └── rust.yml ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Cargo.lock ├── 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/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 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 | RUSTDOCFLAGS: '-D warnings' 13 | 14 | jobs: 15 | test: 16 | name: Test with multiple feature combinations 17 | runs-on: ubuntu-latest 18 | strategy: 19 | matrix: 20 | include: 21 | # 32-bit target 22 | - target: i686-unknown-linux-gnu 23 | deps: sudo apt update && sudo apt install gcc-multilib 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 | with: 30 | target: ${{ matrix.target }} 31 | - run: ${{ matrix.deps }} 32 | - run: cargo test --target ${{ matrix.target }} --no-default-features 33 | - run: cargo test --target ${{ matrix.target }} --no-default-features --features reusable_secrets 34 | - run: cargo test --target ${{ matrix.target }} --no-default-features --features static_secrets 35 | - run: cargo test --target ${{ matrix.target }} 36 | - run: cargo test --target ${{ matrix.target }} --all-features 37 | 38 | build-simd: 39 | name: Test simd backend (nightly) 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v3 43 | - uses: dtolnay/rust-toolchain@nightly 44 | - env: 45 | RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx2' 46 | run: cargo build --target x86_64-unknown-linux-gnu 47 | - env: 48 | RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx512ifma' 49 | run: cargo build --target x86_64-unknown-linux-gnu 50 | 51 | msrv: 52 | name: Current MSRV is 1.60.0 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v3 56 | # First delete the checked-in `Cargo.lock`. We're going to regenerate it 57 | - run: rm Cargo.lock 58 | # Now run `cargo +nightly -Z minimal-verisons check` in order to get a 59 | # Cargo.lock with the oldest possible deps 60 | - uses: dtolnay/rust-toolchain@nightly 61 | - run: cargo -Z minimal-versions check --no-default-features --features serde 62 | # Now check that `cargo build` works with respect to the oldest possible 63 | # deps and the stated MSRV 64 | - uses: dtolnay/rust-toolchain@1.60.0 65 | - run: cargo build 66 | 67 | # no_std support is pending feature, tracking: 68 | # https://github.com/dalek-cryptography/x25519-dalek/issues/111 69 | # # Test no_std integration with no features 70 | # build-nostd-base: 71 | # name: Build on no_std target (thumbv7em-none-eabi) 72 | # runs-on: ubuntu-latest 73 | # steps: 74 | # - uses: actions/checkout@v3 75 | # - uses: dtolnay/rust-toolchain@master 76 | # with: 77 | # toolchain: stable 78 | # targets: thumbv7em-none-eabi 79 | # - uses: taiki-e/install-action@cargo-hack 80 | # # No default features build 81 | # - run: cargo build --target thumbv7em-none-eabi --release --no-default-features 82 | # 83 | # # Test no_std integration with all no_std features 84 | # build-nostd-features: 85 | # name: Build on no_std target (thumbv7em-none-eabi) 86 | # runs-on: ubuntu-latest 87 | # steps: 88 | # - uses: actions/checkout@v3 89 | # - uses: dtolnay/rust-toolchain@master 90 | # with: 91 | # toolchain: stable 92 | # targets: thumbv7em-none-eabi 93 | # - uses: taiki-e/install-action@cargo-hack 94 | # # No default features build 95 | # - run: cargo hack build --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std 96 | 97 | bench: 98 | name: Check that benchmarks compile 99 | runs-on: ubuntu-latest 100 | steps: 101 | - uses: actions/checkout@v3 102 | - uses: dtolnay/rust-toolchain@stable 103 | - run: cargo build --benches 104 | 105 | rustfmt: 106 | name: Check formatting 107 | runs-on: ubuntu-latest 108 | steps: 109 | - uses: actions/checkout@v3 110 | - uses: dtolnay/rust-toolchain@stable 111 | with: 112 | components: rustfmt 113 | - run: cargo fmt --all -- --check 114 | 115 | clippy: 116 | name: Check that clippy is happy 117 | runs-on: ubuntu-latest 118 | steps: 119 | - uses: actions/checkout@v3 120 | - uses: dtolnay/rust-toolchain@1.65 121 | with: 122 | components: clippy 123 | - run: cargo clippy 124 | 125 | doc: 126 | runs-on: ubuntu-latest 127 | steps: 128 | - uses: actions/checkout@v3 129 | - uses: dtolnay/rust-toolchain@stable 130 | with: 131 | toolchain: stable 132 | - run: cargo doc --all-features 133 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | **/*.rs.bk 3 | 4 | .cargo 5 | 6 | *~ 7 | \#* 8 | .\#* 9 | *.swp 10 | *.orig 11 | *.bak 12 | 13 | *.s 14 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | Entries are listed in reverse chronological order. 4 | 5 | # 2.x Series 6 | 7 | * Note: All `x255919-dalek` 2.x releases are in sync with the underlying `curve25519-dalek` 4.x releases. 8 | 9 | ## 2.0.0-rc.3 10 | 11 | * `StaticSecret` serialization and `to_bytes()` no longer returns clamped integers. Clamping is still always done during scalar-point multiplication. 12 | * Update underlying `curve25519_dalek` library to `4.0.0-rc.3`. Notable changes: 13 | * [curve25519-dalek backend] now by default auto selects `simd` backend over `serial` where supported. 14 | 15 | 16 | ## 2.0.0-rc.2 17 | 18 | * Update MSRV to 1.60. 19 | * Update edition to 2021 20 | * Add `.as_bytes()` and `AsRef<[u8]>` for `Shared/StaticSecret` 21 | * Add `getrandom` feature to provide `random_from_rng` constructors 22 | * Make `StaticSecrets` optional via feature `static_secrets` 23 | * Update underlying `curve25519_dalek` library to `4.0.0-rc.2`. Notable changes: 24 | * [curve25519-dalek backend] additive features have been removed in favor of cfg based selection. 25 | * [curve25519-dalek backend] now by default auto selects the appropriate word size over the previous default `32`. 26 | 27 | ## 2.0.0-pre.1 28 | 29 | * Loosen restriction on zeroize dependency version from =1.3 to 1. 30 | * Update MSRV to 1.51. 31 | 32 | ## 2.0.0-pre.0 33 | 34 | * Update `rand_core` dependency to `0.6`. 35 | 36 | # 1.x Series 37 | 38 | ## 1.2 39 | 40 | * Add module documentation for using the bytes-oriented `x25519()` API. 41 | * Add implementation of `zeroize::Zeroize` for `PublicKey`. 42 | * Move unittests to a separate directory. 43 | * Add cargo feature flags `"fiat_u32_backend"` and `"fiat_u64_backend"` for 44 | activating the Fiat crypto field element implementations. 45 | * Fix issue with removed `feature(external_doc)` on nightly compilers. 46 | * Pin `zeroize` to version 1.3 to support a wider range of MSRVs. 47 | * Add CI via Github actions. 48 | * Fix breakage in the serde unittests. 49 | * MSRV is now 1.41 for production and 1.48 for development. 50 | * Add an optional check to `SharedSecret` for contibutory behaviour. 51 | * Add implementation of `ReusableSecret` keys which are non-ephemeral, but which 52 | cannot be serialised to discourage long-term use. 53 | 54 | ## 1.1.1 55 | 56 | * Fix a typo in the README. 57 | 58 | ## 1.1.0 59 | 60 | * Add impls of `PartialEq`, `Eq`, and `Hash` for `PublicKey` (by @jack-michaud) 61 | 62 | ## 1.0.1 63 | 64 | * Update underlying `curve25519_dalek` library to `3.0`. 65 | 66 | ## 1.0.0 67 | 68 | * Widen generic bound on `EphemeralSecret::new` and `StaticSecret::new` to 69 | allow owned as well as borrowed RNGs. 70 | * Add `PublicKey::to_bytes` and `SharedSecret::to_bytes`, returning owned byte 71 | arrays, complementing the existing `as_bytes` methods returning references. 72 | * Remove mention of deprecated `rand_os` crate from examples. 73 | * Clarify `EphemeralSecret`/`StaticSecret` distinction in documentation. 74 | 75 | # Pre-1.0.0 76 | 77 | ## 0.6.0 78 | 79 | * Updates `rand_core` version to `0.5`. 80 | * Adds `serde` support. 81 | * Replaces `clear_on_drop` with `zeroize`. 82 | * Use Rust 2018. 83 | 84 | ## 0.5.2 85 | 86 | * Implement `Clone` for `StaticSecret`. 87 | 88 | ## 0.5.1 89 | 90 | * Implement `Copy, Clone, Debug` for `PublicKey`. 91 | * Remove doctests. 92 | 93 | ## 0.5.0 94 | 95 | * Adds support for static and ephemeral keys. 96 | 97 | [curve25519-dalek backend]: https://github.com/dalek-cryptography/curve25519-dalek/#backends 98 | 99 | -------------------------------------------------------------------------------- /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/x25519-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/x25519-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 @isislovecruft. 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 | 21 | # Code of Conduct 22 | 23 | We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html), 24 | with the following additional clauses: 25 | 26 | * We respect the rights to privacy and anonymity for contributors and people in 27 | the community. If someone wishes to contribute under a pseudonym different to 28 | their primary identity, that wish is to be respected by all contributors. 29 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anes" 7 | version = "0.1.6" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 10 | 11 | [[package]] 12 | name = "atty" 13 | version = "0.2.14" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 16 | dependencies = [ 17 | "hermit-abi 0.1.19", 18 | "libc", 19 | "winapi", 20 | ] 21 | 22 | [[package]] 23 | name = "autocfg" 24 | version = "1.1.0" 25 | source = "registry+https://github.com/rust-lang/crates.io-index" 26 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 27 | 28 | [[package]] 29 | name = "bincode" 30 | version = "1.3.3" 31 | source = "registry+https://github.com/rust-lang/crates.io-index" 32 | checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" 33 | dependencies = [ 34 | "serde", 35 | ] 36 | 37 | [[package]] 38 | name = "bitflags" 39 | version = "1.3.2" 40 | source = "registry+https://github.com/rust-lang/crates.io-index" 41 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 42 | 43 | [[package]] 44 | name = "bumpalo" 45 | version = "3.12.0" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" 48 | 49 | [[package]] 50 | name = "cast" 51 | version = "0.3.0" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 54 | 55 | [[package]] 56 | name = "cfg-if" 57 | version = "1.0.0" 58 | source = "registry+https://github.com/rust-lang/crates.io-index" 59 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 60 | 61 | [[package]] 62 | name = "ciborium" 63 | version = "0.2.0" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f" 66 | dependencies = [ 67 | "ciborium-io", 68 | "ciborium-ll", 69 | "serde", 70 | ] 71 | 72 | [[package]] 73 | name = "ciborium-io" 74 | version = "0.2.0" 75 | source = "registry+https://github.com/rust-lang/crates.io-index" 76 | checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369" 77 | 78 | [[package]] 79 | name = "ciborium-ll" 80 | version = "0.2.0" 81 | source = "registry+https://github.com/rust-lang/crates.io-index" 82 | checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b" 83 | dependencies = [ 84 | "ciborium-io", 85 | "half", 86 | ] 87 | 88 | [[package]] 89 | name = "clap" 90 | version = "3.2.23" 91 | source = "registry+https://github.com/rust-lang/crates.io-index" 92 | checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" 93 | dependencies = [ 94 | "bitflags", 95 | "clap_lex", 96 | "indexmap", 97 | "textwrap", 98 | ] 99 | 100 | [[package]] 101 | name = "clap_lex" 102 | version = "0.2.4" 103 | source = "registry+https://github.com/rust-lang/crates.io-index" 104 | checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" 105 | dependencies = [ 106 | "os_str_bytes", 107 | ] 108 | 109 | [[package]] 110 | name = "cpufeatures" 111 | version = "0.2.8" 112 | source = "registry+https://github.com/rust-lang/crates.io-index" 113 | checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c" 114 | dependencies = [ 115 | "libc", 116 | ] 117 | 118 | [[package]] 119 | name = "criterion" 120 | version = "0.4.0" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb" 123 | dependencies = [ 124 | "anes", 125 | "atty", 126 | "cast", 127 | "ciborium", 128 | "clap", 129 | "criterion-plot", 130 | "itertools", 131 | "lazy_static", 132 | "num-traits", 133 | "oorandom", 134 | "plotters", 135 | "rayon", 136 | "regex", 137 | "serde", 138 | "serde_derive", 139 | "serde_json", 140 | "tinytemplate", 141 | "walkdir", 142 | ] 143 | 144 | [[package]] 145 | name = "criterion-plot" 146 | version = "0.5.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 149 | dependencies = [ 150 | "cast", 151 | "itertools", 152 | ] 153 | 154 | [[package]] 155 | name = "crossbeam-channel" 156 | version = "0.5.7" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c" 159 | dependencies = [ 160 | "cfg-if", 161 | "crossbeam-utils", 162 | ] 163 | 164 | [[package]] 165 | name = "crossbeam-deque" 166 | version = "0.8.3" 167 | source = "registry+https://github.com/rust-lang/crates.io-index" 168 | checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" 169 | dependencies = [ 170 | "cfg-if", 171 | "crossbeam-epoch", 172 | "crossbeam-utils", 173 | ] 174 | 175 | [[package]] 176 | name = "crossbeam-epoch" 177 | version = "0.9.14" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" 180 | dependencies = [ 181 | "autocfg", 182 | "cfg-if", 183 | "crossbeam-utils", 184 | "memoffset", 185 | "scopeguard", 186 | ] 187 | 188 | [[package]] 189 | name = "crossbeam-utils" 190 | version = "0.8.15" 191 | source = "registry+https://github.com/rust-lang/crates.io-index" 192 | checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" 193 | dependencies = [ 194 | "cfg-if", 195 | ] 196 | 197 | [[package]] 198 | name = "curve25519-dalek" 199 | version = "4.0.0-rc.3" 200 | source = "registry+https://github.com/rust-lang/crates.io-index" 201 | checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7" 202 | dependencies = [ 203 | "cfg-if", 204 | "cpufeatures", 205 | "curve25519-dalek-derive", 206 | "fiat-crypto", 207 | "platforms", 208 | "rustc_version", 209 | "serde", 210 | "subtle", 211 | "zeroize", 212 | ] 213 | 214 | [[package]] 215 | name = "curve25519-dalek-derive" 216 | version = "0.1.0" 217 | source = "registry+https://github.com/rust-lang/crates.io-index" 218 | checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b" 219 | dependencies = [ 220 | "proc-macro2", 221 | "quote", 222 | "syn 2.0.12", 223 | ] 224 | 225 | [[package]] 226 | name = "either" 227 | version = "1.8.1" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" 230 | 231 | [[package]] 232 | name = "fiat-crypto" 233 | version = "0.1.19" 234 | source = "registry+https://github.com/rust-lang/crates.io-index" 235 | checksum = "93ace6ec7cc19c8ed33a32eaa9ea692d7faea05006b5356b9e2b668ec4bc3955" 236 | 237 | [[package]] 238 | name = "getrandom" 239 | version = "0.2.8" 240 | source = "registry+https://github.com/rust-lang/crates.io-index" 241 | checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" 242 | dependencies = [ 243 | "cfg-if", 244 | "libc", 245 | "wasi", 246 | ] 247 | 248 | [[package]] 249 | name = "half" 250 | version = "1.8.2" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" 253 | 254 | [[package]] 255 | name = "hashbrown" 256 | version = "0.12.3" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 259 | 260 | [[package]] 261 | name = "hermit-abi" 262 | version = "0.1.19" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 265 | dependencies = [ 266 | "libc", 267 | ] 268 | 269 | [[package]] 270 | name = "hermit-abi" 271 | version = "0.2.6" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" 274 | dependencies = [ 275 | "libc", 276 | ] 277 | 278 | [[package]] 279 | name = "indexmap" 280 | version = "1.9.3" 281 | source = "registry+https://github.com/rust-lang/crates.io-index" 282 | checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" 283 | dependencies = [ 284 | "autocfg", 285 | "hashbrown", 286 | ] 287 | 288 | [[package]] 289 | name = "itertools" 290 | version = "0.10.5" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 293 | dependencies = [ 294 | "either", 295 | ] 296 | 297 | [[package]] 298 | name = "itoa" 299 | version = "1.0.6" 300 | source = "registry+https://github.com/rust-lang/crates.io-index" 301 | checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" 302 | 303 | [[package]] 304 | name = "js-sys" 305 | version = "0.3.61" 306 | source = "registry+https://github.com/rust-lang/crates.io-index" 307 | checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" 308 | dependencies = [ 309 | "wasm-bindgen", 310 | ] 311 | 312 | [[package]] 313 | name = "lazy_static" 314 | version = "1.4.0" 315 | source = "registry+https://github.com/rust-lang/crates.io-index" 316 | checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" 317 | 318 | [[package]] 319 | name = "libc" 320 | version = "0.2.140" 321 | source = "registry+https://github.com/rust-lang/crates.io-index" 322 | checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" 323 | 324 | [[package]] 325 | name = "log" 326 | version = "0.4.17" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 329 | dependencies = [ 330 | "cfg-if", 331 | ] 332 | 333 | [[package]] 334 | name = "memoffset" 335 | version = "0.8.0" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" 338 | dependencies = [ 339 | "autocfg", 340 | ] 341 | 342 | [[package]] 343 | name = "num-traits" 344 | version = "0.2.15" 345 | source = "registry+https://github.com/rust-lang/crates.io-index" 346 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 347 | dependencies = [ 348 | "autocfg", 349 | ] 350 | 351 | [[package]] 352 | name = "num_cpus" 353 | version = "1.15.0" 354 | source = "registry+https://github.com/rust-lang/crates.io-index" 355 | checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" 356 | dependencies = [ 357 | "hermit-abi 0.2.6", 358 | "libc", 359 | ] 360 | 361 | [[package]] 362 | name = "once_cell" 363 | version = "1.17.1" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 366 | 367 | [[package]] 368 | name = "oorandom" 369 | version = "11.1.3" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 372 | 373 | [[package]] 374 | name = "os_str_bytes" 375 | version = "6.5.0" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" 378 | 379 | [[package]] 380 | name = "platforms" 381 | version = "3.0.2" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630" 384 | 385 | [[package]] 386 | name = "plotters" 387 | version = "0.3.4" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97" 390 | dependencies = [ 391 | "num-traits", 392 | "plotters-backend", 393 | "plotters-svg", 394 | "wasm-bindgen", 395 | "web-sys", 396 | ] 397 | 398 | [[package]] 399 | name = "plotters-backend" 400 | version = "0.3.4" 401 | source = "registry+https://github.com/rust-lang/crates.io-index" 402 | checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142" 403 | 404 | [[package]] 405 | name = "plotters-svg" 406 | version = "0.3.3" 407 | source = "registry+https://github.com/rust-lang/crates.io-index" 408 | checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f" 409 | dependencies = [ 410 | "plotters-backend", 411 | ] 412 | 413 | [[package]] 414 | name = "proc-macro2" 415 | version = "1.0.54" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534" 418 | dependencies = [ 419 | "unicode-ident", 420 | ] 421 | 422 | [[package]] 423 | name = "quote" 424 | version = "1.0.26" 425 | source = "registry+https://github.com/rust-lang/crates.io-index" 426 | checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" 427 | dependencies = [ 428 | "proc-macro2", 429 | ] 430 | 431 | [[package]] 432 | name = "rand_core" 433 | version = "0.6.4" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 436 | dependencies = [ 437 | "getrandom", 438 | ] 439 | 440 | [[package]] 441 | name = "rayon" 442 | version = "1.7.0" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b" 445 | dependencies = [ 446 | "either", 447 | "rayon-core", 448 | ] 449 | 450 | [[package]] 451 | name = "rayon-core" 452 | version = "1.11.0" 453 | source = "registry+https://github.com/rust-lang/crates.io-index" 454 | checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d" 455 | dependencies = [ 456 | "crossbeam-channel", 457 | "crossbeam-deque", 458 | "crossbeam-utils", 459 | "num_cpus", 460 | ] 461 | 462 | [[package]] 463 | name = "regex" 464 | version = "1.7.3" 465 | source = "registry+https://github.com/rust-lang/crates.io-index" 466 | checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" 467 | dependencies = [ 468 | "regex-syntax", 469 | ] 470 | 471 | [[package]] 472 | name = "regex-syntax" 473 | version = "0.6.29" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" 476 | 477 | [[package]] 478 | name = "rustc_version" 479 | version = "0.4.0" 480 | source = "registry+https://github.com/rust-lang/crates.io-index" 481 | checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" 482 | dependencies = [ 483 | "semver", 484 | ] 485 | 486 | [[package]] 487 | name = "ryu" 488 | version = "1.0.13" 489 | source = "registry+https://github.com/rust-lang/crates.io-index" 490 | checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" 491 | 492 | [[package]] 493 | name = "same-file" 494 | version = "1.0.6" 495 | source = "registry+https://github.com/rust-lang/crates.io-index" 496 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 497 | dependencies = [ 498 | "winapi-util", 499 | ] 500 | 501 | [[package]] 502 | name = "scopeguard" 503 | version = "1.1.0" 504 | source = "registry+https://github.com/rust-lang/crates.io-index" 505 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 506 | 507 | [[package]] 508 | name = "semver" 509 | version = "1.0.17" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" 512 | 513 | [[package]] 514 | name = "serde" 515 | version = "1.0.159" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065" 518 | dependencies = [ 519 | "serde_derive", 520 | ] 521 | 522 | [[package]] 523 | name = "serde_derive" 524 | version = "1.0.159" 525 | source = "registry+https://github.com/rust-lang/crates.io-index" 526 | checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585" 527 | dependencies = [ 528 | "proc-macro2", 529 | "quote", 530 | "syn 2.0.12", 531 | ] 532 | 533 | [[package]] 534 | name = "serde_json" 535 | version = "1.0.95" 536 | source = "registry+https://github.com/rust-lang/crates.io-index" 537 | checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744" 538 | dependencies = [ 539 | "itoa", 540 | "ryu", 541 | "serde", 542 | ] 543 | 544 | [[package]] 545 | name = "subtle" 546 | version = "2.5.0" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" 549 | 550 | [[package]] 551 | name = "syn" 552 | version = "1.0.109" 553 | source = "registry+https://github.com/rust-lang/crates.io-index" 554 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 555 | dependencies = [ 556 | "proc-macro2", 557 | "quote", 558 | "unicode-ident", 559 | ] 560 | 561 | [[package]] 562 | name = "syn" 563 | version = "2.0.12" 564 | source = "registry+https://github.com/rust-lang/crates.io-index" 565 | checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927" 566 | dependencies = [ 567 | "proc-macro2", 568 | "quote", 569 | "unicode-ident", 570 | ] 571 | 572 | [[package]] 573 | name = "textwrap" 574 | version = "0.16.0" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" 577 | 578 | [[package]] 579 | name = "tinytemplate" 580 | version = "1.2.1" 581 | source = "registry+https://github.com/rust-lang/crates.io-index" 582 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 583 | dependencies = [ 584 | "serde", 585 | "serde_json", 586 | ] 587 | 588 | [[package]] 589 | name = "unicode-ident" 590 | version = "1.0.8" 591 | source = "registry+https://github.com/rust-lang/crates.io-index" 592 | checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" 593 | 594 | [[package]] 595 | name = "walkdir" 596 | version = "2.3.3" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698" 599 | dependencies = [ 600 | "same-file", 601 | "winapi-util", 602 | ] 603 | 604 | [[package]] 605 | name = "wasi" 606 | version = "0.11.0+wasi-snapshot-preview1" 607 | source = "registry+https://github.com/rust-lang/crates.io-index" 608 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 609 | 610 | [[package]] 611 | name = "wasm-bindgen" 612 | version = "0.2.84" 613 | source = "registry+https://github.com/rust-lang/crates.io-index" 614 | checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" 615 | dependencies = [ 616 | "cfg-if", 617 | "wasm-bindgen-macro", 618 | ] 619 | 620 | [[package]] 621 | name = "wasm-bindgen-backend" 622 | version = "0.2.84" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" 625 | dependencies = [ 626 | "bumpalo", 627 | "log", 628 | "once_cell", 629 | "proc-macro2", 630 | "quote", 631 | "syn 1.0.109", 632 | "wasm-bindgen-shared", 633 | ] 634 | 635 | [[package]] 636 | name = "wasm-bindgen-macro" 637 | version = "0.2.84" 638 | source = "registry+https://github.com/rust-lang/crates.io-index" 639 | checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" 640 | dependencies = [ 641 | "quote", 642 | "wasm-bindgen-macro-support", 643 | ] 644 | 645 | [[package]] 646 | name = "wasm-bindgen-macro-support" 647 | version = "0.2.84" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" 650 | dependencies = [ 651 | "proc-macro2", 652 | "quote", 653 | "syn 1.0.109", 654 | "wasm-bindgen-backend", 655 | "wasm-bindgen-shared", 656 | ] 657 | 658 | [[package]] 659 | name = "wasm-bindgen-shared" 660 | version = "0.2.84" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" 663 | 664 | [[package]] 665 | name = "web-sys" 666 | version = "0.3.61" 667 | source = "registry+https://github.com/rust-lang/crates.io-index" 668 | checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" 669 | dependencies = [ 670 | "js-sys", 671 | "wasm-bindgen", 672 | ] 673 | 674 | [[package]] 675 | name = "winapi" 676 | version = "0.3.9" 677 | source = "registry+https://github.com/rust-lang/crates.io-index" 678 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 679 | dependencies = [ 680 | "winapi-i686-pc-windows-gnu", 681 | "winapi-x86_64-pc-windows-gnu", 682 | ] 683 | 684 | [[package]] 685 | name = "winapi-i686-pc-windows-gnu" 686 | version = "0.4.0" 687 | source = "registry+https://github.com/rust-lang/crates.io-index" 688 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 689 | 690 | [[package]] 691 | name = "winapi-util" 692 | version = "0.1.5" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 695 | dependencies = [ 696 | "winapi", 697 | ] 698 | 699 | [[package]] 700 | name = "winapi-x86_64-pc-windows-gnu" 701 | version = "0.4.0" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 704 | 705 | [[package]] 706 | name = "x25519-dalek" 707 | version = "2.0.0-rc.3" 708 | dependencies = [ 709 | "bincode", 710 | "criterion", 711 | "curve25519-dalek", 712 | "rand_core", 713 | "serde", 714 | "zeroize", 715 | ] 716 | 717 | [[package]] 718 | name = "zeroize" 719 | version = "1.6.0" 720 | source = "registry+https://github.com/rust-lang/crates.io-index" 721 | checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" 722 | dependencies = [ 723 | "zeroize_derive", 724 | ] 725 | 726 | [[package]] 727 | name = "zeroize_derive" 728 | version = "1.4.2" 729 | source = "registry+https://github.com/rust-lang/crates.io-index" 730 | checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" 731 | dependencies = [ 732 | "proc-macro2", 733 | "quote", 734 | "syn 2.0.12", 735 | ] 736 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "x25519-dalek" 3 | edition = "2021" 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 = "2.0.0-rc.3" 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/x25519-dalek" 18 | homepage = "https://dalek.rs/" 19 | documentation = "https://docs.rs/x25519-dalek" 20 | categories = ["cryptography", "no-std"] 21 | keywords = ["cryptography", "curve25519", "key-exchange", "x25519", "diffie-hellman"] 22 | description = "X25519 elliptic curve Diffie-Hellman key exchange in pure-Rust, using curve25519-dalek." 23 | exclude = [ 24 | ".gitignore", 25 | ".travis.yml", 26 | "CONTRIBUTING.md", 27 | ] 28 | rust-version = "1.60" 29 | 30 | [badges] 31 | travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"} 32 | 33 | [package.metadata.docs.rs] 34 | rustdoc-args = [ 35 | "--html-in-header", "docs/assets/rustdoc-include-katex-header.html", 36 | "--cfg", "docsrs", 37 | ] 38 | features = ["getrandom", "reusable_secrets", "serde", "static_secrets"] 39 | 40 | [dependencies] 41 | curve25519-dalek = { version = "=4.0.0-rc.3", default-features = false } 42 | rand_core = { version = "0.6", default-features = false } 43 | serde = { version = "1", default-features = false, optional = true, features = ["derive"] } 44 | zeroize = { version = "1", default-features = false, optional = true, features = ["zeroize_derive"] } 45 | 46 | [dev-dependencies] 47 | bincode = "1" 48 | criterion = "0.4.0" 49 | rand_core = { version = "0.6", default-features = false, features = ["getrandom"] } 50 | 51 | [[bench]] 52 | name = "x25519" 53 | harness = false 54 | 55 | [features] 56 | default = ["alloc", "precomputed-tables", "zeroize"] 57 | getrandom = ["rand_core/getrandom"] 58 | zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"] 59 | serde = ["dep:serde", "curve25519-dalek/serde"] 60 | alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"] 61 | precomputed-tables = ["curve25519-dalek/precomputed-tables"] 62 | reusable_secrets = [] 63 | static_secrets = [] 64 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ARCHIVED 2 | 3 | This repo has been [moved](https://github.com/dalek-cryptography/curve25519-dalek). Please direct all issues and pull requests to the new repo. 4 | 5 | This repo will remain here in a read-only state for historical purposes. 6 | 7 | --- 8 | 9 | # x25519-dalek [![](https://img.shields.io/crates/v/x25519-dalek.svg)](https://crates.io/crates/x25519-dalek) [![](https://docs.rs/x25519-dalek/badge.svg)](https://docs.rs/x25519-dalek) [![](https://travis-ci.org/dalek-cryptography/x25519-dalek.svg?branch=master)](https://travis-ci.org/dalek-cryptography/x25519-dalek) 10 | 11 | A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange, 12 | with curve operations provided by 13 | [curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek). 14 | 15 | This crate provides two levels of API: a bare byte-oriented `x25519` 16 | function which matches the function specified in [RFC7748][rfc7748], as 17 | well as a higher-level Rust API for static and ephemeral Diffie-Hellman. 18 | 19 | ## Examples 20 | 21 | 22 | 25 | 26 | 27 | Alice and Bob are two adorable kittens who have lost their mittens, and they 28 | wish to be able to send secret messages to each other to coordinate finding 29 | them, otherwise—if their caretaker cat finds out—they will surely be called 30 | naughty kittens and be given no pie! 31 | 32 | But the two kittens are quite clever. Even though their paws are still too big 33 | and the rest of them is 90% fuzziness, these clever kittens have been studying 34 | up on modern public key cryptography and have learned a nifty trick called 35 | *elliptic curve Diffie-Hellman key exchange*. With the right incantations, the 36 | kittens will be able to secretly organise to find their mittens, and then spend 37 | the rest of the afternoon nomming some yummy pie! 38 | 39 | First, Alice uses `EphemeralSecret::random()` and then 40 | `PublicKey::from()` to produce her secret and public keys: 41 | 42 | ```ignore 43 | use x25519_dalek::{EphemeralSecret, PublicKey}; 44 | 45 | let alice_secret = EphemeralSecret::random(); 46 | let alice_public = PublicKey::from(&alice_secret); 47 | ``` 48 | 49 | Bob does the same: 50 | 51 | ```ignore 52 | # use x25519_dalek::{EphemeralSecret, PublicKey}; 53 | let bob_secret = EphemeralSecret::random(); 54 | let bob_public = PublicKey::from(&bob_secret); 55 | ``` 56 | 57 | Alice meows across the room, telling `alice_public` to Bob, and Bob 58 | loudly meows `bob_public` back to Alice. Alice now computes her 59 | shared secret with Bob by doing: 60 | 61 | ```rust 62 | # use rand_core::OsRng; 63 | # use x25519_dalek::{EphemeralSecret, PublicKey}; 64 | # let alice_secret = EphemeralSecret::new(OsRng); 65 | # let alice_public = PublicKey::from(&alice_secret); 66 | # let bob_secret = EphemeralSecret::new(OsRng); 67 | # let bob_public = PublicKey::from(&bob_secret); 68 | let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); 69 | ``` 70 | 71 | Similarly, Bob computes a shared secret by doing: 72 | 73 | ```rust 74 | # use rand_core::OsRng; 75 | # use x25519_dalek::{EphemeralSecret, PublicKey}; 76 | # let alice_secret = EphemeralSecret::new(OsRng); 77 | # let alice_public = PublicKey::from(&alice_secret); 78 | # let bob_secret = EphemeralSecret::new(OsRng); 79 | # let bob_public = PublicKey::from(&bob_secret); 80 | let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); 81 | ``` 82 | 83 | These secrets are the same: 84 | 85 | ```rust 86 | # use rand_core::OsRng; 87 | # use x25519_dalek::{EphemeralSecret, PublicKey}; 88 | # let alice_secret = EphemeralSecret::new(OsRng); 89 | # let alice_public = PublicKey::from(&alice_secret); 90 | # let bob_secret = EphemeralSecret::new(OsRng); 91 | # let bob_public = PublicKey::from(&bob_secret); 92 | # let alice_shared_secret = alice_secret.diffie_hellman(&bob_public); 93 | # let bob_shared_secret = bob_secret.diffie_hellman(&alice_public); 94 | assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes()); 95 | ``` 96 | 97 | Voilà! Alice and Bob can now use their shared secret to encrypt their 98 | meows, for example, by using it to generate a key and nonce for an 99 | authenticated-encryption cipher. 100 | 101 | This example used the ephemeral DH API, which ensures that secret keys 102 | cannot be reused; Alice and Bob could instead use the static DH API 103 | and load a long-term secret key. 104 | 105 | # Installation 106 | 107 | To install, add the following to your project's `Cargo.toml`: 108 | 109 | ```toml 110 | [dependencies] 111 | x25519-dalek = "2.0.0-rc.3" 112 | ``` 113 | 114 | # MSRV 115 | 116 | Current MSRV is 1.60. 117 | 118 | # Documentation 119 | 120 | Documentation is available [here](https://docs.rs/x25519-dalek). 121 | 122 | # Performance and backend selection 123 | 124 | 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]). 125 | 126 | Further instructions and details regarding backends can be found in the [curve25519-dalek docs](https://github.com/dalek-cryptography/curve25519-dalek#backends). 127 | 128 | # Note 129 | 130 | This code matches the [RFC7748][rfc7748] test vectors. 131 | The elliptic curve 132 | operations are provided by `curve25519-dalek`, which makes a best-effort 133 | attempt to prevent software side-channels. 134 | 135 | "Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine) 136 | copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg)) 137 | 138 | [rfc7748]: https://tools.ietf.org/html/rfc7748 139 | 140 | # See also 141 | 142 | - [crypto_box]: pure Rust public-key authenticated encryption compatible with 143 | the NaCl family of encryption libraries (libsodium, TweetNaCl) which uses 144 | `x25519-dalek` for key agreement 145 | 146 | [fiat]: https://github.com/mit-plv/fiat-crypto 147 | [crypto_box]: https://github.com/RustCrypto/nacl-compat/tree/master/crypto_box 148 | -------------------------------------------------------------------------------- /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_group, criterion_main, Criterion}; 15 | 16 | use rand_core::OsRng; 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(OsRng); 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(OsRng), 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 | -------------------------------------------------------------------------------- /docs/assets/rustdoc-include-katex-header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 13 | -------------------------------------------------------------------------------- /res/bubblesort-zines-secret-messages-cover.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalek-cryptography/x25519-dalek/2ddda07333e5028698d8618428c783264ae38b40/res/bubblesort-zines-secret-messages-cover.jpeg -------------------------------------------------------------------------------- /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(feature = "bench", feature(test))] 19 | #![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))] 20 | #![cfg_attr(docsrs, doc(cfg_hide(docsrs)))] 21 | #![deny(missing_docs)] 22 | #![doc( 23 | html_logo_url = "https://cdn.jsdelivr.net/gh/dalek-cryptography/curve25519-dalek/docs/assets/dalek-logo-clear.png" 24 | )] 25 | #![doc = include_str!("../README.md")] 26 | 27 | //------------------------------------------------------------------------ 28 | // x25519-dalek public API 29 | //------------------------------------------------------------------------ 30 | 31 | mod x25519; 32 | 33 | pub use crate::x25519::*; 34 | -------------------------------------------------------------------------------- /src/x25519.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 | //! x25519 Diffie-Hellman key exchange 13 | //! 14 | //! This implements x25519 key exchange as specified by Mike Hamburg 15 | //! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748). 16 | 17 | use curve25519_dalek::{edwards::EdwardsPoint, montgomery::MontgomeryPoint, traits::IsIdentity}; 18 | 19 | use rand_core::CryptoRng; 20 | use rand_core::RngCore; 21 | 22 | #[cfg(feature = "zeroize")] 23 | use zeroize::Zeroize; 24 | 25 | /// A Diffie-Hellman public key 26 | /// 27 | /// We implement `Zeroize` so that downstream consumers may derive it for `Drop` 28 | /// should they wish to erase public keys from memory. Note that this erasure 29 | /// (in this crate) does *not* automatically happen, but either must be derived 30 | /// for Drop or explicitly called. 31 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 32 | #[cfg_attr(feature = "zeroize", derive(Zeroize))] 33 | #[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)] 34 | pub struct PublicKey(pub(crate) MontgomeryPoint); 35 | 36 | impl From<[u8; 32]> for PublicKey { 37 | /// Given a byte array, construct a x25519 `PublicKey`. 38 | fn from(bytes: [u8; 32]) -> PublicKey { 39 | PublicKey(MontgomeryPoint(bytes)) 40 | } 41 | } 42 | 43 | impl PublicKey { 44 | /// Convert this public key to a byte array. 45 | #[inline] 46 | pub fn to_bytes(&self) -> [u8; 32] { 47 | self.0.to_bytes() 48 | } 49 | 50 | /// View this public key as a byte array. 51 | #[inline] 52 | pub fn as_bytes(&self) -> &[u8; 32] { 53 | self.0.as_bytes() 54 | } 55 | } 56 | 57 | impl AsRef<[u8]> for PublicKey { 58 | /// View this public key as a byte array. 59 | #[inline] 60 | fn as_ref(&self) -> &[u8] { 61 | self.as_bytes() 62 | } 63 | } 64 | 65 | /// A short-lived Diffie-Hellman secret key that can only be used to compute a single 66 | /// [`SharedSecret`]. 67 | /// 68 | /// This type is identical to the `StaticSecret` type, except that the 69 | /// [`EphemeralSecret::diffie_hellman`] method consumes and then wipes the secret key, and there 70 | /// are no serialization methods defined. This means that [`EphemeralSecret`]s can only be 71 | /// generated from fresh randomness where the compiler statically checks that the resulting 72 | /// secret is used at most once. 73 | #[cfg_attr(feature = "zeroize", derive(Zeroize))] 74 | #[cfg_attr(feature = "zeroize", zeroize(drop))] 75 | pub struct EphemeralSecret(pub(crate) [u8; 32]); 76 | 77 | impl EphemeralSecret { 78 | /// Perform a Diffie-Hellman key agreement between `self` and 79 | /// `their_public` key to produce a [`SharedSecret`]. 80 | pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret { 81 | SharedSecret(their_public.0.mul_clamped(self.0)) 82 | } 83 | 84 | /// Generate a new [`EphemeralSecret`] with the supplied RNG. 85 | #[deprecated( 86 | since = "2.0.0", 87 | note = "Renamed to `random_from_rng`. This will be removed in 2.1.0" 88 | )] 89 | pub fn new(mut csprng: T) -> Self { 90 | Self::random_from_rng(&mut csprng) 91 | } 92 | 93 | /// Generate a new [`EphemeralSecret`] with the supplied RNG. 94 | pub fn random_from_rng(mut csprng: T) -> Self { 95 | // The secret key is random bytes. Clamping is done later. 96 | let mut bytes = [0u8; 32]; 97 | csprng.fill_bytes(&mut bytes); 98 | EphemeralSecret(bytes) 99 | } 100 | 101 | /// Generate a new [`EphemeralSecret`]. 102 | #[cfg(feature = "getrandom")] 103 | pub fn random() -> Self { 104 | Self::random_from_rng(&mut rand_core::OsRng) 105 | } 106 | } 107 | 108 | impl<'a> From<&'a EphemeralSecret> for PublicKey { 109 | /// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`]. 110 | fn from(secret: &'a EphemeralSecret) -> PublicKey { 111 | PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery()) 112 | } 113 | } 114 | 115 | /// A Diffie-Hellman secret key which may be used more than once, but is 116 | /// purposefully not serialiseable in order to discourage key-reuse. This is 117 | /// implemented to facilitate protocols such as Noise (e.g. Noise IK key usage, 118 | /// etc.) and X3DH which require an "ephemeral" key to conduct the 119 | /// Diffie-Hellman operation multiple times throughout the protocol, while the 120 | /// protocol run at a higher level is only conducted once per key. 121 | /// 122 | /// Similarly to [`EphemeralSecret`], this type does _not_ have serialisation 123 | /// methods, in order to discourage long-term usage of secret key material. (For 124 | /// long-term secret keys, see `StaticSecret`.) 125 | /// 126 | /// # Warning 127 | /// 128 | /// If you're uncertain about whether you should use this, then you likely 129 | /// should not be using this. Our strongly recommended advice is to use 130 | /// [`EphemeralSecret`] at all times, as that type enforces at compile-time that 131 | /// secret keys are never reused, which can have very serious security 132 | /// implications for many protocols. 133 | #[cfg(feature = "reusable_secrets")] 134 | #[cfg_attr(feature = "zeroize", derive(Zeroize))] 135 | #[cfg_attr(feature = "zeroize", zeroize(drop))] 136 | #[derive(Clone)] 137 | pub struct ReusableSecret(pub(crate) [u8; 32]); 138 | 139 | #[cfg(feature = "reusable_secrets")] 140 | impl ReusableSecret { 141 | /// Perform a Diffie-Hellman key agreement between `self` and 142 | /// `their_public` key to produce a [`SharedSecret`]. 143 | pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { 144 | SharedSecret(their_public.0.mul_clamped(self.0)) 145 | } 146 | 147 | /// Generate a new [`ReusableSecret`] with the supplied RNG. 148 | #[deprecated( 149 | since = "2.0.0", 150 | note = "Renamed to `random_from_rng`. This will be removed in 2.1.0." 151 | )] 152 | pub fn new(mut csprng: T) -> Self { 153 | Self::random_from_rng(&mut csprng) 154 | } 155 | 156 | /// Generate a new [`ReusableSecret`] with the supplied RNG. 157 | pub fn random_from_rng(mut csprng: T) -> Self { 158 | // The secret key is random bytes. Clamping is done later. 159 | let mut bytes = [0u8; 32]; 160 | csprng.fill_bytes(&mut bytes); 161 | ReusableSecret(bytes) 162 | } 163 | 164 | /// Generate a new [`ReusableSecret`]. 165 | #[cfg(feature = "getrandom")] 166 | pub fn random() -> Self { 167 | Self::random_from_rng(&mut rand_core::OsRng) 168 | } 169 | } 170 | 171 | #[cfg(feature = "reusable_secrets")] 172 | impl<'a> From<&'a ReusableSecret> for PublicKey { 173 | /// Given an x25519 [`ReusableSecret`] key, compute its corresponding [`PublicKey`]. 174 | fn from(secret: &'a ReusableSecret) -> PublicKey { 175 | PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery()) 176 | } 177 | } 178 | 179 | /// A Diffie-Hellman secret key that can be used to compute multiple [`SharedSecret`]s. 180 | /// 181 | /// This type is identical to the [`EphemeralSecret`] type, except that the 182 | /// [`StaticSecret::diffie_hellman`] method does not consume the secret key, and the type provides 183 | /// serialization methods to save and load key material. This means that the secret may be used 184 | /// multiple times (but does not *have to be*). 185 | /// 186 | /// # Warning 187 | /// 188 | /// If you're uncertain about whether you should use this, then you likely 189 | /// should not be using this. Our strongly recommended advice is to use 190 | /// [`EphemeralSecret`] at all times, as that type enforces at compile-time that 191 | /// secret keys are never reused, which can have very serious security 192 | /// implications for many protocols. 193 | #[cfg(feature = "static_secrets")] 194 | #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 195 | #[cfg_attr(feature = "zeroize", derive(Zeroize))] 196 | #[cfg_attr(feature = "zeroize", zeroize(drop))] 197 | #[derive(Clone)] 198 | pub struct StaticSecret([u8; 32]); 199 | 200 | #[cfg(feature = "static_secrets")] 201 | impl StaticSecret { 202 | /// Perform a Diffie-Hellman key agreement between `self` and 203 | /// `their_public` key to produce a `SharedSecret`. 204 | pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret { 205 | SharedSecret(their_public.0.mul_clamped(self.0)) 206 | } 207 | 208 | /// Generate a new [`StaticSecret`] with the supplied RNG. 209 | #[deprecated( 210 | since = "2.0.0", 211 | note = "Renamed to `random_from_rng`. This will be removed in 2.1.0" 212 | )] 213 | pub fn new(mut csprng: T) -> Self { 214 | Self::random_from_rng(&mut csprng) 215 | } 216 | 217 | /// Generate a new [`StaticSecret`] with the supplied RNG. 218 | pub fn random_from_rng(mut csprng: T) -> Self { 219 | // The secret key is random bytes. Clamping is done later. 220 | let mut bytes = [0u8; 32]; 221 | csprng.fill_bytes(&mut bytes); 222 | StaticSecret(bytes) 223 | } 224 | 225 | /// Generate a new [`StaticSecret`]. 226 | #[cfg(feature = "getrandom")] 227 | pub fn random() -> Self { 228 | Self::random_from_rng(&mut rand_core::OsRng) 229 | } 230 | 231 | /// Extract this key's bytes for serialization. 232 | #[inline] 233 | pub fn to_bytes(&self) -> [u8; 32] { 234 | self.0 235 | } 236 | 237 | /// View this key as a byte array. 238 | #[inline] 239 | pub fn as_bytes(&self) -> &[u8; 32] { 240 | &self.0 241 | } 242 | } 243 | 244 | #[cfg(feature = "static_secrets")] 245 | impl From<[u8; 32]> for StaticSecret { 246 | /// Load a secret key from a byte array. 247 | fn from(bytes: [u8; 32]) -> StaticSecret { 248 | StaticSecret(bytes) 249 | } 250 | } 251 | 252 | #[cfg(feature = "static_secrets")] 253 | impl<'a> From<&'a StaticSecret> for PublicKey { 254 | /// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`]. 255 | fn from(secret: &'a StaticSecret) -> PublicKey { 256 | PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery()) 257 | } 258 | } 259 | 260 | #[cfg(feature = "static_secrets")] 261 | impl AsRef<[u8]> for StaticSecret { 262 | /// View this key as a byte array. 263 | #[inline] 264 | fn as_ref(&self) -> &[u8] { 265 | self.as_bytes() 266 | } 267 | } 268 | 269 | /// The result of a Diffie-Hellman key exchange. 270 | /// 271 | /// Each party computes this using their [`EphemeralSecret`] or [`StaticSecret`] and their 272 | /// counterparty's [`PublicKey`]. 273 | #[cfg_attr(feature = "zeroize", derive(Zeroize))] 274 | #[cfg_attr(feature = "zeroize", zeroize(drop))] 275 | pub struct SharedSecret(pub(crate) MontgomeryPoint); 276 | 277 | impl SharedSecret { 278 | /// Convert this shared secret to a byte array. 279 | #[inline] 280 | pub fn to_bytes(&self) -> [u8; 32] { 281 | self.0.to_bytes() 282 | } 283 | 284 | /// View this shared secret key as a byte array. 285 | #[inline] 286 | pub fn as_bytes(&self) -> &[u8; 32] { 287 | self.0.as_bytes() 288 | } 289 | 290 | /// Ensure in constant-time that this shared secret did not result from a 291 | /// key exchange with non-contributory behaviour. 292 | /// 293 | /// In some more exotic protocols which need to guarantee "contributory" 294 | /// behaviour for both parties, that is, that each party contributed a public 295 | /// value which increased the security of the resulting shared secret. 296 | /// To take an example protocol attack where this could lead to undesirable 297 | /// results [from Thái "thaidn" Dương](https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html): 298 | /// 299 | /// > If Mallory replaces Alice's and Bob's public keys with zero, which is 300 | /// > a valid Curve25519 public key, he would be able to force the ECDH 301 | /// > shared value to be zero, which is the encoding of the point at infinity, 302 | /// > and thus get to dictate some publicly known values as the shared 303 | /// > keys. It still requires an active man-in-the-middle attack to pull the 304 | /// > trick, after which, however, not only Mallory can decode Alice's data, 305 | /// > but everyone too! It is also impossible for Alice and Bob to detect the 306 | /// > intrusion, as they still share the same keys, and can communicate with 307 | /// > each other as normal. 308 | /// 309 | /// The original Curve25519 specification argues that checks for 310 | /// non-contributory behaviour are "unnecessary for Diffie-Hellman". 311 | /// Whether this check is necessary for any particular given protocol is 312 | /// often a matter of debate, which we will not re-hash here, but simply 313 | /// cite some of the [relevant] [public] [discussions]. 314 | /// 315 | /// # Returns 316 | /// 317 | /// Returns `true` if the key exchange was contributory (good), and `false` 318 | /// otherwise (can be bad for some protocols). 319 | /// 320 | /// [relevant]: https://tools.ietf.org/html/rfc7748#page-15 321 | /// [public]: https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html 322 | /// [discussions]: https://vnhacker.blogspot.com/2016/08/the-internet-of-broken-protocols.html 323 | #[must_use] 324 | pub fn was_contributory(&self) -> bool { 325 | !self.0.is_identity() 326 | } 327 | } 328 | 329 | impl AsRef<[u8]> for SharedSecret { 330 | /// View this shared secret key as a byte array. 331 | #[inline] 332 | fn as_ref(&self) -> &[u8] { 333 | self.as_bytes() 334 | } 335 | } 336 | 337 | /// The bare, byte-oriented x25519 function, exactly as specified in RFC7748. 338 | /// 339 | /// This can be used with [`X25519_BASEPOINT_BYTES`] for people who 340 | /// cannot use the better, safer, and faster ephemeral DH API. 341 | /// 342 | /// # Example 343 | #[cfg_attr(feature = "static_secrets", doc = "```")] 344 | #[cfg_attr(not(feature = "static_secrets"), doc = "```ignore")] 345 | /// use rand_core::OsRng; 346 | /// use rand_core::RngCore; 347 | /// 348 | /// use x25519_dalek::x25519; 349 | /// use x25519_dalek::StaticSecret; 350 | /// use x25519_dalek::PublicKey; 351 | /// 352 | /// // Generate Alice's key pair. 353 | /// let alice_secret = StaticSecret::random_from_rng(&mut OsRng); 354 | /// let alice_public = PublicKey::from(&alice_secret); 355 | /// 356 | /// // Generate Bob's key pair. 357 | /// let bob_secret = StaticSecret::random_from_rng(&mut OsRng); 358 | /// let bob_public = PublicKey::from(&bob_secret); 359 | /// 360 | /// // Alice and Bob should now exchange their public keys. 361 | /// 362 | /// // Once they've done so, they may generate a shared secret. 363 | /// let alice_shared = x25519(alice_secret.to_bytes(), bob_public.to_bytes()); 364 | /// let bob_shared = x25519(bob_secret.to_bytes(), alice_public.to_bytes()); 365 | /// 366 | /// assert_eq!(alice_shared, bob_shared); 367 | /// ``` 368 | pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { 369 | MontgomeryPoint(u).mul_clamped(k).to_bytes() 370 | } 371 | 372 | /// The X25519 basepoint, for use with the bare, byte-oriented x25519 373 | /// function. This is provided for people who cannot use the typed 374 | /// DH API for some reason. 375 | pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ 376 | 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 377 | ]; 378 | -------------------------------------------------------------------------------- /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; 185 | 186 | #[test] 187 | fn ephemeral_from_rng() { 188 | #[allow(deprecated)] 189 | EphemeralSecret::new(OsRng); 190 | EphemeralSecret::random_from_rng(OsRng); 191 | } 192 | 193 | #[test] 194 | #[cfg(feature = "reusable_secrets")] 195 | fn reusable_from_rng() { 196 | #[allow(deprecated)] 197 | ReusableSecret::new(OsRng); 198 | ReusableSecret::random_from_rng(OsRng); 199 | } 200 | 201 | #[test] 202 | #[cfg(feature = "static_secrets")] 203 | fn static_from_rng() { 204 | #[allow(deprecated)] 205 | StaticSecret::new(OsRng); 206 | StaticSecret::random_from_rng(OsRng); 207 | } 208 | } 209 | 210 | #[cfg(feature = "getrandom")] 211 | mod getrandom { 212 | 213 | use super::*; 214 | 215 | #[test] 216 | fn ephemeral_random() { 217 | EphemeralSecret::random(); 218 | } 219 | 220 | #[test] 221 | #[cfg(feature = "reusable_secrets")] 222 | fn reusable_random() { 223 | ReusableSecret::random(); 224 | } 225 | 226 | #[test] 227 | #[cfg(feature = "static_secrets")] 228 | fn static_random() { 229 | StaticSecret::random(); 230 | } 231 | } 232 | --------------------------------------------------------------------------------