Merge remote-tracking branch 'x/main' into monorepo-t1

This commit is contained in:
pinkforest 2023-06-27 04:06:50 +00:00
commit e7ecb74ed0
No known key found for this signature in database
15 changed files with 1957 additions and 0 deletions

132
.github/workflows/rust.yml vendored Normal file
View file

@ -0,0 +1,132 @@
name: Rust
on:
push:
branches: [ '**' ]
pull_request:
branches: [ '**' ]
env:
CARGO_TERM_COLOR: always
RUSTFLAGS: '-D warnings'
RUSTDOCFLAGS: '-D warnings'
jobs:
test:
name: Test with multiple feature combinations
runs-on: ubuntu-latest
strategy:
matrix:
include:
# 32-bit target
- target: i686-unknown-linux-gnu
deps: sudo apt update && sudo apt install gcc-multilib
# 64-bit target
- target: x86_64-unknown-linux-gnu
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
target: ${{ matrix.target }}
- run: ${{ matrix.deps }}
- run: cargo test --target ${{ matrix.target }} --no-default-features
- run: cargo test --target ${{ matrix.target }} --no-default-features --features reusable_secrets
- run: cargo test --target ${{ matrix.target }} --no-default-features --features static_secrets
- run: cargo test --target ${{ matrix.target }}
- run: cargo test --target ${{ matrix.target }} --all-features
build-simd:
name: Test simd backend (nightly)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx2'
run: cargo build --target x86_64-unknown-linux-gnu
- env:
RUSTFLAGS: '--cfg curve25519_dalek_backend="simd" -C target_feature=+avx512ifma'
run: cargo build --target x86_64-unknown-linux-gnu
msrv:
name: Current MSRV is 1.60.0
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
# First delete the checked-in `Cargo.lock`. We're going to regenerate it
- run: rm Cargo.lock
# Now run `cargo +nightly -Z minimal-verisons check` in order to get a
# Cargo.lock with the oldest possible deps
- uses: dtolnay/rust-toolchain@nightly
- run: cargo -Z minimal-versions check --no-default-features --features serde
# Now check that `cargo build` works with respect to the oldest possible
# deps and the stated MSRV
- uses: dtolnay/rust-toolchain@1.60.0
- run: cargo build
# no_std support is pending feature, tracking:
# https://github.com/dalek-cryptography/x25519-dalek/issues/111
# # Test no_std integration with no features
# build-nostd-base:
# name: Build on no_std target (thumbv7em-none-eabi)
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: dtolnay/rust-toolchain@master
# with:
# toolchain: stable
# targets: thumbv7em-none-eabi
# - uses: taiki-e/install-action@cargo-hack
# # No default features build
# - run: cargo build --target thumbv7em-none-eabi --release --no-default-features
#
# # Test no_std integration with all no_std features
# build-nostd-features:
# name: Build on no_std target (thumbv7em-none-eabi)
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v3
# - uses: dtolnay/rust-toolchain@master
# with:
# toolchain: stable
# targets: thumbv7em-none-eabi
# - uses: taiki-e/install-action@cargo-hack
# # No default features build
# - run: cargo hack build --target thumbv7em-none-eabi --release --each-feature --exclude-features default,std
bench:
name: Check that benchmarks compile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- run: cargo build --benches
rustfmt:
name: Check formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
- run: cargo fmt --all -- --check
clippy:
name: Check that clippy is happy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@1.65
with:
components: clippy
- run: cargo clippy
doc:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
- run: cargo doc --all-features

13
.gitignore vendored Normal file
View file

@ -0,0 +1,13 @@
target/
**/*.rs.bk
.cargo
*~
\#*
.\#*
*.swp
*.orig
*.bak
*.s

26
.travis.yml Normal file
View file

@ -0,0 +1,26 @@
language: rust
rust:
- nightly
env:
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='default'
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly'
- TEST_COMMAND=bench EXTRA_FLAGS='' FEATURES='default'
- TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u32_backend nightly'
- TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u64_backend nightly'
matrix:
include:
- rust: stable
env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u64_backend'
- rust: beta
env: TEST_COMMAND=test EXTRA_FLAGS='--no-default-features' FEATURES='std u64_backend'
script:
- cargo $TEST_COMMAND --features="$FEATURES" $EXTRA_FLAGS
notifications:
slack:
rooms:
- dalek-cryptography:Xxv9WotKYWdSoKlgKNqXiHoD#dalek-bots

98
CHANGELOG.md Normal file
View file

@ -0,0 +1,98 @@
# Changelog
Entries are listed in reverse chronological order.
# 2.x Series
* Note: All `x255919-dalek` 2.x releases are in sync with the underlying `curve25519-dalek` 4.x releases.
## 2.0.0-rc.3
* `StaticSecret` serialization and `to_bytes()` no longer returns clamped integers. Clamping is still always done during scalar-point multiplication.
* Update underlying `curve25519_dalek` library to `4.0.0-rc.3`. Notable changes:
* [curve25519-dalek backend] now by default auto selects `simd` backend over `serial` where supported.
## 2.0.0-rc.2
* Update MSRV to 1.60.
* Update edition to 2021
* Add `.as_bytes()` and `AsRef<[u8]>` for `Shared/StaticSecret`
* Add `getrandom` feature to provide `random_from_rng` constructors
* Make `StaticSecrets` optional via feature `static_secrets`
* Update underlying `curve25519_dalek` library to `4.0.0-rc.2`. Notable changes:
* [curve25519-dalek backend] additive features have been removed in favor of cfg based selection.
* [curve25519-dalek backend] now by default auto selects the appropriate word size over the previous default `32`.
## 2.0.0-pre.1
* Loosen restriction on zeroize dependency version from =1.3 to 1.
* Update MSRV to 1.51.
## 2.0.0-pre.0
* Update `rand_core` dependency to `0.6`.
# 1.x Series
## 1.2
* Add module documentation for using the bytes-oriented `x25519()` API.
* Add implementation of `zeroize::Zeroize` for `PublicKey`.
* Move unittests to a separate directory.
* Add cargo feature flags `"fiat_u32_backend"` and `"fiat_u64_backend"` for
activating the Fiat crypto field element implementations.
* Fix issue with removed `feature(external_doc)` on nightly compilers.
* Pin `zeroize` to version 1.3 to support a wider range of MSRVs.
* Add CI via Github actions.
* Fix breakage in the serde unittests.
* MSRV is now 1.41 for production and 1.48 for development.
* Add an optional check to `SharedSecret` for contibutory behaviour.
* Add implementation of `ReusableSecret` keys which are non-ephemeral, but which
cannot be serialised to discourage long-term use.
## 1.1.1
* Fix a typo in the README.
## 1.1.0
* Add impls of `PartialEq`, `Eq`, and `Hash` for `PublicKey` (by @jack-michaud)
## 1.0.1
* Update underlying `curve25519_dalek` library to `3.0`.
## 1.0.0
* Widen generic bound on `EphemeralSecret::new` and `StaticSecret::new` to
allow owned as well as borrowed RNGs.
* Add `PublicKey::to_bytes` and `SharedSecret::to_bytes`, returning owned byte
arrays, complementing the existing `as_bytes` methods returning references.
* Remove mention of deprecated `rand_os` crate from examples.
* Clarify `EphemeralSecret`/`StaticSecret` distinction in documentation.
# Pre-1.0.0
## 0.6.0
* Updates `rand_core` version to `0.5`.
* Adds `serde` support.
* Replaces `clear_on_drop` with `zeroize`.
* Use Rust 2018.
## 0.5.2
* Implement `Clone` for `StaticSecret`.
## 0.5.1
* Implement `Copy, Clone, Debug` for `PublicKey`.
* Remove doctests.
## 0.5.0
* Adds support for static and ephemeral keys.
[curve25519-dalek backend]: https://github.com/dalek-cryptography/curve25519-dalek/#backends

28
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,28 @@
# Contributing to curve25519-dalek
If you have questions or comments, please feel free to email the
authors.
For feature requests, suggestions, and bug reports, please open an issue on
[our Github](https://github.com/dalek-cryptography/x25519-dalek). (Or, send us
an email if you're opposed to using Github for whatever reason.)
Patches are welcomed as pull requests on
[our Github](https://github.com/dalek-cryptography/x25519-dalek), as well as by
email (preferably sent to all of the authors listed in `Cargo.toml`).
All issues on curve25519-dalek are mentored, if you want help with a bug just
ask @isislovecruft.
Some issues are easier than others. The `easy` label can be used to find the
easy issues. If you want to work on an issue, please leave a comment so that we
can assign it to you!
# Code of Conduct
We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html),
with the following additional clauses:
* We respect the rights to privacy and anonymity for contributors and people in
the community. If someone wishes to contribute under a pseudonym different to
their primary identity, that wish is to be respected by all contributors.

735
Cargo.lock generated Normal file
View file

@ -0,0 +1,735 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anes"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299"
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi 0.1.19",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bincode"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
dependencies = [
"serde",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bumpalo"
version = "3.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535"
[[package]]
name = "cast"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "ciborium"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0c137568cc60b904a7724001b35ce2630fd00d5d84805fbb608ab89509d788f"
dependencies = [
"ciborium-io",
"ciborium-ll",
"serde",
]
[[package]]
name = "ciborium-io"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "346de753af073cc87b52b2083a506b38ac176a44cfb05497b622e27be899b369"
[[package]]
name = "ciborium-ll"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213030a2b5a4e0c0892b6652260cf6ccac84827b83a85a534e178e3906c4cf1b"
dependencies = [
"ciborium-io",
"half",
]
[[package]]
name = "clap"
version = "3.2.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5"
dependencies = [
"bitflags",
"clap_lex",
"indexmap",
"textwrap",
]
[[package]]
name = "clap_lex"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5"
dependencies = [
"os_str_bytes",
]
[[package]]
name = "cpufeatures"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03e69e28e9f7f77debdedbaafa2866e1de9ba56df55a8bd7cfc724c25a09987c"
dependencies = [
"libc",
]
[[package]]
name = "criterion"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7c76e09c1aae2bc52b3d2f29e13c6572553b30c4aa1b8a49fd70de6412654cb"
dependencies = [
"anes",
"atty",
"cast",
"ciborium",
"clap",
"criterion-plot",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
dependencies = [
"cfg-if",
]
[[package]]
name = "curve25519-dalek"
version = "4.0.0-rc.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7"
dependencies = [
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"fiat-crypto",
"platforms",
"rustc_version",
"serde",
"subtle",
"zeroize",
]
[[package]]
name = "curve25519-dalek-derive"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83fdaf97f4804dcebfa5862639bc9ce4121e82140bec2a987ac5140294865b5b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
]
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "fiat-crypto"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ace6ec7cc19c8ed33a32eaa9ea692d7faea05006b5356b9e2b668ec4bc3955"
[[package]]
name = "getrandom"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "hermit-abi"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itertools"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6"
[[package]]
name = "js-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.140"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
[[package]]
name = "log"
version = "0.4.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
dependencies = [
"cfg-if",
]
[[package]]
name = "memoffset"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
dependencies = [
"autocfg",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
dependencies = [
"hermit-abi 0.2.6",
"libc",
]
[[package]]
name = "once_cell"
version = "1.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "os_str_bytes"
version = "6.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267"
[[package]]
name = "platforms"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630"
[[package]]
name = "plotters"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2538b639e642295546c50fcd545198c9d64ee2a38620a628724a3b266d5fbf97"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "193228616381fecdc1224c62e96946dfbc73ff4384fba576e052ff8c1bea8142"
[[package]]
name = "plotters-svg"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a81d2759aae1dae668f783c308bc5c8ebd191ff4184aaa1b37f65a6ae5a56f"
dependencies = [
"plotters-backend",
]
[[package]]
name = "proc-macro2"
version = "1.0.54"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e472a104799c74b514a57226160104aa483546de37e839ec50e3c2e41dd87534"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]]
name = "rayon"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"num_cpus",
]
[[package]]
name = "regex"
version = "1.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
[[package]]
name = "serde"
version = "1.0.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.159"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
]
[[package]]
name = "serde_json"
version = "1.0.95"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "subtle"
version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79d9531f94112cfc3e4c8f5f02cb2b58f72c97b7efd85f70203cc6d8efda5927"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d"
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-ident"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "walkdir"
version = "2.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
dependencies = [
"same-file",
"winapi-util",
]
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn 1.0.109",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6"
dependencies = [
"proc-macro2",
"quote",
"syn 1.0.109",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d"
[[package]]
name = "web-sys"
version = "0.3.61"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "x25519-dalek"
version = "2.0.0-rc.3"
dependencies = [
"bincode",
"criterion",
"curve25519-dalek",
"rand_core",
"serde",
"zeroize",
]
[[package]]
name = "zeroize"
version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9"
dependencies = [
"zeroize_derive",
]
[[package]]
name = "zeroize_derive"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.12",
]

63
Cargo.toml Normal file
View file

@ -0,0 +1,63 @@
[package]
name = "x25519-dalek"
edition = "2021"
# Before changing this:
# - update version in README.md
# - update html_root_url
# - update CHANGELOG
# - if any changes were made to README.md, mirror them in src/lib.rs docs
version = "2.0.0-rc.3"
authors = [
"Isis Lovecruft <isis@patternsinthevoid.net>",
"DebugSteven <debugsteven@gmail.com>",
"Henry de Valence <hdevalence@hdevalence.ca>",
]
readme = "README.md"
license = "BSD-3-Clause"
repository = "https://github.com/dalek-cryptography/x25519-dalek"
homepage = "https://dalek.rs/"
documentation = "https://docs.rs/x25519-dalek"
categories = ["cryptography", "no-std"]
keywords = ["cryptography", "curve25519", "key-exchange", "x25519", "diffie-hellman"]
description = "X25519 elliptic curve Diffie-Hellman key exchange in pure-Rust, using curve25519-dalek."
exclude = [
".gitignore",
".travis.yml",
"CONTRIBUTING.md",
]
rust-version = "1.60"
[badges]
travis-ci = { repository = "dalek-cryptography/x25519-dalek", branch = "master"}
[package.metadata.docs.rs]
rustdoc-args = [
"--html-in-header", "docs/assets/rustdoc-include-katex-header.html",
"--cfg", "docsrs",
]
features = ["getrandom", "reusable_secrets", "serde", "static_secrets"]
[dependencies]
curve25519-dalek = { version = "=4.0.0-rc.3", default-features = false }
rand_core = { version = "0.6", default-features = false }
serde = { version = "1", default-features = false, optional = true, features = ["derive"] }
zeroize = { version = "1", default-features = false, optional = true, features = ["zeroize_derive"] }
[dev-dependencies]
bincode = "1"
criterion = "0.4.0"
rand_core = { version = "0.6", default-features = false, features = ["getrandom"] }
[[bench]]
name = "x25519"
harness = false
[features]
default = ["alloc", "precomputed-tables", "zeroize"]
getrandom = ["rand_core/getrandom"]
zeroize = ["dep:zeroize", "curve25519-dalek/zeroize"]
serde = ["dep:serde", "curve25519-dalek/serde"]
alloc = ["curve25519-dalek/alloc", "serde?/alloc", "zeroize?/alloc"]
precomputed-tables = ["curve25519-dalek/precomputed-tables"]
reusable_secrets = []
static_secrets = []

29
LICENSE Normal file
View file

@ -0,0 +1,29 @@
Copyright (c) 2017-2021 isis agora lovecruft. All rights reserved.
Copyright (c) 2019-2021 DebugSteven. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

139
README.md Normal file
View file

@ -0,0 +1,139 @@
# 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)
A pure-Rust implementation of x25519 elliptic curve Diffie-Hellman key exchange,
with curve operations provided by
[curve25519-dalek](https://github.com/dalek-cryptography/curve25519-dalek).
This crate provides two levels of API: a bare byte-oriented `x25519`
function which matches the function specified in [RFC7748][rfc7748], as
well as a higher-level Rust API for static and ephemeral Diffie-Hellman.
## Examples
<a href="https://shop.bubblesort.io">
<img
style="float: right; width: auto; height: 300px;"
src="https://raw.githubusercontent.com/dalek-cryptography/x25519-dalek/master/res/bubblesort-zines-secret-messages-cover.jpeg"/>
</a>
Alice and Bob are two adorable kittens who have lost their mittens, and they
wish to be able to send secret messages to each other to coordinate finding
them, otherwise—if their caretaker cat finds out—they will surely be called
naughty kittens and be given no pie!
But the two kittens are quite clever. Even though their paws are still too big
and the rest of them is 90% fuzziness, these clever kittens have been studying
up on modern public key cryptography and have learned a nifty trick called
*elliptic curve Diffie-Hellman key exchange*. With the right incantations, the
kittens will be able to secretly organise to find their mittens, and then spend
the rest of the afternoon nomming some yummy pie!
First, Alice uses `EphemeralSecret::random()` and then
`PublicKey::from()` to produce her secret and public keys:
```ignore
use x25519_dalek::{EphemeralSecret, PublicKey};
let alice_secret = EphemeralSecret::random();
let alice_public = PublicKey::from(&alice_secret);
```
Bob does the same:
```ignore
# use x25519_dalek::{EphemeralSecret, PublicKey};
let bob_secret = EphemeralSecret::random();
let bob_public = PublicKey::from(&bob_secret);
```
Alice meows across the room, telling `alice_public` to Bob, and Bob
loudly meows `bob_public` back to Alice. Alice now computes her
shared secret with Bob by doing:
```rust
# use rand_core::OsRng;
# use x25519_dalek::{EphemeralSecret, PublicKey};
# let alice_secret = EphemeralSecret::new(OsRng);
# let alice_public = PublicKey::from(&alice_secret);
# let bob_secret = EphemeralSecret::new(OsRng);
# let bob_public = PublicKey::from(&bob_secret);
let alice_shared_secret = alice_secret.diffie_hellman(&bob_public);
```
Similarly, Bob computes a shared secret by doing:
```rust
# use rand_core::OsRng;
# use x25519_dalek::{EphemeralSecret, PublicKey};
# let alice_secret = EphemeralSecret::new(OsRng);
# let alice_public = PublicKey::from(&alice_secret);
# let bob_secret = EphemeralSecret::new(OsRng);
# let bob_public = PublicKey::from(&bob_secret);
let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);
```
These secrets are the same:
```rust
# use rand_core::OsRng;
# use x25519_dalek::{EphemeralSecret, PublicKey};
# let alice_secret = EphemeralSecret::new(OsRng);
# let alice_public = PublicKey::from(&alice_secret);
# let bob_secret = EphemeralSecret::new(OsRng);
# let bob_public = PublicKey::from(&bob_secret);
# let alice_shared_secret = alice_secret.diffie_hellman(&bob_public);
# let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);
assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes());
```
Voilà! Alice and Bob can now use their shared secret to encrypt their
meows, for example, by using it to generate a key and nonce for an
authenticated-encryption cipher.
This example used the ephemeral DH API, which ensures that secret keys
cannot be reused; Alice and Bob could instead use the static DH API
and load a long-term secret key.
# Installation
To install, add the following to your project's `Cargo.toml`:
```toml
[dependencies]
x25519-dalek = "2.0.0-rc.3"
```
# MSRV
Current MSRV is 1.60.
# Documentation
Documentation is available [here](https://docs.rs/x25519-dalek).
# Performance and backend selection
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]).
Further instructions and details regarding backends can be found in the [curve25519-dalek docs](https://github.com/dalek-cryptography/curve25519-dalek#backends).
# Note
This code matches the [RFC7748][rfc7748] test vectors.
The elliptic curve
operations are provided by `curve25519-dalek`, which makes a best-effort
attempt to prevent software side-channels.
"Secret Messages" cover image and [zine](https://shop.bubblesort.io/products/secret-messages-zine)
copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg))
[rfc7748]: https://tools.ietf.org/html/rfc7748
# See also
- [crypto_box]: pure Rust public-key authenticated encryption compatible with
the NaCl family of encryption libraries (libsodium, TweetNaCl) which uses
`x25519-dalek` for key agreement
[fiat]: https://github.com/mit-plv/fiat-crypto
[crypto_box]: https://github.com/RustCrypto/nacl-compat/tree/master/crypto_box

41
benches/x25519.rs Normal file
View file

@ -0,0 +1,41 @@
// -*- mode: rust; -*-
//
// This file is part of x25519-dalek.
// Copyright (c) 2017-2019 isis agora lovecruft
// Copyright (c) 2019 DebugSteven
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - DebugSteven <debugsteven@gmail.com>
//! Benchmark the Diffie-Hellman operation.
use criterion::{criterion_group, criterion_main, Criterion};
use rand_core::OsRng;
use x25519_dalek::EphemeralSecret;
use x25519_dalek::PublicKey;
fn bench_diffie_hellman(c: &mut Criterion) {
let bob_secret = EphemeralSecret::random_from_rng(OsRng);
let bob_public = PublicKey::from(&bob_secret);
c.bench_function("diffie_hellman", move |b| {
b.iter_with_setup(
|| EphemeralSecret::random_from_rng(OsRng),
|alice_secret| alice_secret.diffie_hellman(&bob_public),
)
});
}
criterion_group! {
name = x25519_benches;
config = Criterion::default();
targets =
bench_diffie_hellman,
}
criterion_main! {
x25519_benches,
}

View file

@ -0,0 +1,12 @@
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/katex.min.css" integrity="sha384-Juol1FqnotbkyZUT5Z7gUPjQ9gzlwCENvUZTpQBAPxtusdwFLRy382PSDx5UUJ4/" crossorigin="anonymous">
<!-- The loading of KaTeX is deferred to speed up page rendering -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/katex.min.js" integrity="sha384-97gW6UIJxnlKemYavrqDHSX3SiygeOwIZhwyOKRfSaf0JWKRVj9hLASHgFTzT+0O" crossorigin="anonymous"></script>
<!-- To automatically render math in text elements, include the auto-render extension: -->
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.3/dist/contrib/auto-render.min.js" integrity="sha384-+VBxd3r6XgURycqtZ117nYw44OOcIax56Z4dCRWbxyPt0Koah1uHoK0o4+/RRE05" crossorigin="anonymous"
onload="renderMathInElement(document.body);"></script>
<style>
.katex { font-size: 1em !important; }
</style>

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

33
src/lib.rs Normal file
View file

@ -0,0 +1,33 @@
// -*- mode: rust; -*-
//
// This file is part of x25519-dalek.
// Copyright (c) 2017-2021 isis lovecruft
// Copyright (c) 2019-2021 DebugSteven
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - DebugSteven <debugsteven@gmail.com>
// Refuse to compile if documentation is missing, but only on nightly.
//
// This means that missing docs will still fail CI, but means we can use
// README.md as the crate documentation.
#![no_std]
#![cfg_attr(feature = "bench", feature(test))]
#![cfg_attr(docsrs, feature(doc_auto_cfg, doc_cfg, doc_cfg_hide))]
#![cfg_attr(docsrs, doc(cfg_hide(docsrs)))]
#![deny(missing_docs)]
#![doc(
html_logo_url = "https://cdn.jsdelivr.net/gh/dalek-cryptography/curve25519-dalek/docs/assets/dalek-logo-clear.png"
)]
#![doc = include_str!("../README.md")]
//------------------------------------------------------------------------
// x25519-dalek public API
//------------------------------------------------------------------------
mod x25519;
pub use crate::x25519::*;

377
src/x25519.rs Normal file
View file

@ -0,0 +1,377 @@
// -*- mode: rust; -*-
//
// This file is part of x25519-dalek.
// Copyright (c) 2017-2021 isis lovecruft
// Copyright (c) 2019-2021 DebugSteven
// See LICENSE for licensing information.
//
// Authors:
// - isis agora lovecruft <isis@patternsinthevoid.net>
// - DebugSteven <debugsteven@gmail.com>
//! x25519 Diffie-Hellman key exchange
//!
//! This implements x25519 key exchange as specified by Mike Hamburg
//! and Adam Langley in [RFC7748](https://tools.ietf.org/html/rfc7748).
use curve25519_dalek::{edwards::EdwardsPoint, montgomery::MontgomeryPoint, traits::IsIdentity};
use rand_core::CryptoRng;
use rand_core::RngCore;
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// A Diffie-Hellman public key
///
/// We implement `Zeroize` so that downstream consumers may derive it for `Drop`
/// should they wish to erase public keys from memory. Note that this erasure
/// (in this crate) does *not* automatically happen, but either must be derived
/// for Drop or explicitly called.
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
pub struct PublicKey(pub(crate) MontgomeryPoint);
impl From<[u8; 32]> for PublicKey {
/// Given a byte array, construct a x25519 `PublicKey`.
fn from(bytes: [u8; 32]) -> PublicKey {
PublicKey(MontgomeryPoint(bytes))
}
}
impl PublicKey {
/// Convert this public key to a byte array.
#[inline]
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
}
/// View this public key as a byte array.
#[inline]
pub fn as_bytes(&self) -> &[u8; 32] {
self.0.as_bytes()
}
}
impl AsRef<[u8]> for PublicKey {
/// View this public key as a byte array.
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
/// A short-lived Diffie-Hellman secret key that can only be used to compute a single
/// [`SharedSecret`].
///
/// This type is identical to the `StaticSecret` type, except that the
/// [`EphemeralSecret::diffie_hellman`] method consumes and then wipes the secret key, and there
/// are no serialization methods defined. This means that [`EphemeralSecret`]s can only be
/// generated from fresh randomness where the compiler statically checks that the resulting
/// secret is used at most once.
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
#[cfg_attr(feature = "zeroize", zeroize(drop))]
pub struct EphemeralSecret(pub(crate) [u8; 32]);
impl EphemeralSecret {
/// Perform a Diffie-Hellman key agreement between `self` and
/// `their_public` key to produce a [`SharedSecret`].
pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
SharedSecret(their_public.0.mul_clamped(self.0))
}
/// Generate a new [`EphemeralSecret`] with the supplied RNG.
#[deprecated(
since = "2.0.0",
note = "Renamed to `random_from_rng`. This will be removed in 2.1.0"
)]
pub fn new<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
Self::random_from_rng(&mut csprng)
}
/// Generate a new [`EphemeralSecret`] with the supplied RNG.
pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
// The secret key is random bytes. Clamping is done later.
let mut bytes = [0u8; 32];
csprng.fill_bytes(&mut bytes);
EphemeralSecret(bytes)
}
/// Generate a new [`EphemeralSecret`].
#[cfg(feature = "getrandom")]
pub fn random() -> Self {
Self::random_from_rng(&mut rand_core::OsRng)
}
}
impl<'a> From<&'a EphemeralSecret> for PublicKey {
/// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`].
fn from(secret: &'a EphemeralSecret) -> PublicKey {
PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery())
}
}
/// A Diffie-Hellman secret key which may be used more than once, but is
/// purposefully not serialiseable in order to discourage key-reuse. This is
/// implemented to facilitate protocols such as Noise (e.g. Noise IK key usage,
/// etc.) and X3DH which require an "ephemeral" key to conduct the
/// Diffie-Hellman operation multiple times throughout the protocol, while the
/// protocol run at a higher level is only conducted once per key.
///
/// Similarly to [`EphemeralSecret`], this type does _not_ have serialisation
/// methods, in order to discourage long-term usage of secret key material. (For
/// long-term secret keys, see `StaticSecret`.)
///
/// # Warning
///
/// If you're uncertain about whether you should use this, then you likely
/// should not be using this. Our strongly recommended advice is to use
/// [`EphemeralSecret`] at all times, as that type enforces at compile-time that
/// secret keys are never reused, which can have very serious security
/// implications for many protocols.
#[cfg(feature = "reusable_secrets")]
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
#[cfg_attr(feature = "zeroize", zeroize(drop))]
#[derive(Clone)]
pub struct ReusableSecret(pub(crate) [u8; 32]);
#[cfg(feature = "reusable_secrets")]
impl ReusableSecret {
/// Perform a Diffie-Hellman key agreement between `self` and
/// `their_public` key to produce a [`SharedSecret`].
pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret {
SharedSecret(their_public.0.mul_clamped(self.0))
}
/// Generate a new [`ReusableSecret`] with the supplied RNG.
#[deprecated(
since = "2.0.0",
note = "Renamed to `random_from_rng`. This will be removed in 2.1.0."
)]
pub fn new<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
Self::random_from_rng(&mut csprng)
}
/// Generate a new [`ReusableSecret`] with the supplied RNG.
pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
// The secret key is random bytes. Clamping is done later.
let mut bytes = [0u8; 32];
csprng.fill_bytes(&mut bytes);
ReusableSecret(bytes)
}
/// Generate a new [`ReusableSecret`].
#[cfg(feature = "getrandom")]
pub fn random() -> Self {
Self::random_from_rng(&mut rand_core::OsRng)
}
}
#[cfg(feature = "reusable_secrets")]
impl<'a> From<&'a ReusableSecret> for PublicKey {
/// Given an x25519 [`ReusableSecret`] key, compute its corresponding [`PublicKey`].
fn from(secret: &'a ReusableSecret) -> PublicKey {
PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery())
}
}
/// A Diffie-Hellman secret key that can be used to compute multiple [`SharedSecret`]s.
///
/// This type is identical to the [`EphemeralSecret`] type, except that the
/// [`StaticSecret::diffie_hellman`] method does not consume the secret key, and the type provides
/// serialization methods to save and load key material. This means that the secret may be used
/// multiple times (but does not *have to be*).
///
/// # Warning
///
/// If you're uncertain about whether you should use this, then you likely
/// should not be using this. Our strongly recommended advice is to use
/// [`EphemeralSecret`] at all times, as that type enforces at compile-time that
/// secret keys are never reused, which can have very serious security
/// implications for many protocols.
#[cfg(feature = "static_secrets")]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
#[cfg_attr(feature = "zeroize", zeroize(drop))]
#[derive(Clone)]
pub struct StaticSecret([u8; 32]);
#[cfg(feature = "static_secrets")]
impl StaticSecret {
/// Perform a Diffie-Hellman key agreement between `self` and
/// `their_public` key to produce a `SharedSecret`.
pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret {
SharedSecret(their_public.0.mul_clamped(self.0))
}
/// Generate a new [`StaticSecret`] with the supplied RNG.
#[deprecated(
since = "2.0.0",
note = "Renamed to `random_from_rng`. This will be removed in 2.1.0"
)]
pub fn new<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
Self::random_from_rng(&mut csprng)
}
/// Generate a new [`StaticSecret`] with the supplied RNG.
pub fn random_from_rng<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
// The secret key is random bytes. Clamping is done later.
let mut bytes = [0u8; 32];
csprng.fill_bytes(&mut bytes);
StaticSecret(bytes)
}
/// Generate a new [`StaticSecret`].
#[cfg(feature = "getrandom")]
pub fn random() -> Self {
Self::random_from_rng(&mut rand_core::OsRng)
}
/// Extract this key's bytes for serialization.
#[inline]
pub fn to_bytes(&self) -> [u8; 32] {
self.0
}
/// View this key as a byte array.
#[inline]
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
#[cfg(feature = "static_secrets")]
impl From<[u8; 32]> for StaticSecret {
/// Load a secret key from a byte array.
fn from(bytes: [u8; 32]) -> StaticSecret {
StaticSecret(bytes)
}
}
#[cfg(feature = "static_secrets")]
impl<'a> From<&'a StaticSecret> for PublicKey {
/// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`].
fn from(secret: &'a StaticSecret) -> PublicKey {
PublicKey(EdwardsPoint::mul_base_clamped(secret.0).to_montgomery())
}
}
#[cfg(feature = "static_secrets")]
impl AsRef<[u8]> for StaticSecret {
/// View this key as a byte array.
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
/// The result of a Diffie-Hellman key exchange.
///
/// Each party computes this using their [`EphemeralSecret`] or [`StaticSecret`] and their
/// counterparty's [`PublicKey`].
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
#[cfg_attr(feature = "zeroize", zeroize(drop))]
pub struct SharedSecret(pub(crate) MontgomeryPoint);
impl SharedSecret {
/// Convert this shared secret to a byte array.
#[inline]
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
}
/// View this shared secret key as a byte array.
#[inline]
pub fn as_bytes(&self) -> &[u8; 32] {
self.0.as_bytes()
}
/// Ensure in constant-time that this shared secret did not result from a
/// key exchange with non-contributory behaviour.
///
/// In some more exotic protocols which need to guarantee "contributory"
/// behaviour for both parties, that is, that each party contributed a public
/// value which increased the security of the resulting shared secret.
/// To take an example protocol attack where this could lead to undesirable
/// results [from Thái "thaidn" Dương](https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html):
///
/// > If Mallory replaces Alice's and Bob's public keys with zero, which is
/// > a valid Curve25519 public key, he would be able to force the ECDH
/// > shared value to be zero, which is the encoding of the point at infinity,
/// > and thus get to dictate some publicly known values as the shared
/// > keys. It still requires an active man-in-the-middle attack to pull the
/// > trick, after which, however, not only Mallory can decode Alice's data,
/// > but everyone too! It is also impossible for Alice and Bob to detect the
/// > intrusion, as they still share the same keys, and can communicate with
/// > each other as normal.
///
/// The original Curve25519 specification argues that checks for
/// non-contributory behaviour are "unnecessary for Diffie-Hellman".
/// Whether this check is necessary for any particular given protocol is
/// often a matter of debate, which we will not re-hash here, but simply
/// cite some of the [relevant] [public] [discussions].
///
/// # Returns
///
/// Returns `true` if the key exchange was contributory (good), and `false`
/// otherwise (can be bad for some protocols).
///
/// [relevant]: https://tools.ietf.org/html/rfc7748#page-15
/// [public]: https://vnhacker.blogspot.com/2015/09/why-not-validating-curve25519-public.html
/// [discussions]: https://vnhacker.blogspot.com/2016/08/the-internet-of-broken-protocols.html
#[must_use]
pub fn was_contributory(&self) -> bool {
!self.0.is_identity()
}
}
impl AsRef<[u8]> for SharedSecret {
/// View this shared secret key as a byte array.
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
/// The bare, byte-oriented x25519 function, exactly as specified in RFC7748.
///
/// This can be used with [`X25519_BASEPOINT_BYTES`] for people who
/// cannot use the better, safer, and faster ephemeral DH API.
///
/// # Example
#[cfg_attr(feature = "static_secrets", doc = "```")]
#[cfg_attr(not(feature = "static_secrets"), doc = "```ignore")]
/// use rand_core::OsRng;
/// use rand_core::RngCore;
///
/// use x25519_dalek::x25519;
/// use x25519_dalek::StaticSecret;
/// use x25519_dalek::PublicKey;
///
/// // Generate Alice's key pair.
/// let alice_secret = StaticSecret::random_from_rng(&mut OsRng);
/// let alice_public = PublicKey::from(&alice_secret);
///
/// // Generate Bob's key pair.
/// let bob_secret = StaticSecret::random_from_rng(&mut OsRng);
/// let bob_public = PublicKey::from(&bob_secret);
///
/// // Alice and Bob should now exchange their public keys.
///
/// // Once they've done so, they may generate a shared secret.
/// let alice_shared = x25519(alice_secret.to_bytes(), bob_public.to_bytes());
/// let bob_shared = x25519(bob_secret.to_bytes(), alice_public.to_bytes());
///
/// assert_eq!(alice_shared, bob_shared);
/// ```
pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] {
MontgomeryPoint(u).mul_clamped(k).to_bytes()
}
/// The X25519 basepoint, for use with the bare, byte-oriented x25519
/// function. This is provided for people who cannot use the typed
/// DH API for some reason.
pub const X25519_BASEPOINT_BYTES: [u8; 32] = [
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,
];

231
tests/x25519_tests.rs Normal file
View file

@ -0,0 +1,231 @@
use curve25519_dalek::edwards::EdwardsPoint;
use x25519_dalek::*;
#[test]
fn byte_basepoint_matches_edwards_scalar_mul() {
let mut scalar_bytes = [0x37; 32];
for i in 0..32 {
scalar_bytes[i] += 2;
let result = x25519(scalar_bytes, X25519_BASEPOINT_BYTES);
let expected = EdwardsPoint::mul_base_clamped(scalar_bytes)
.to_montgomery()
.to_bytes();
assert_eq!(result, expected);
}
}
#[test]
#[cfg(feature = "serde")]
fn serde_bincode_public_key_roundtrip() {
use bincode;
let public_key = PublicKey::from(X25519_BASEPOINT_BYTES);
let encoded = bincode::serialize(&public_key).unwrap();
let decoded: PublicKey = bincode::deserialize(&encoded).unwrap();
assert_eq!(encoded.len(), 32);
assert_eq!(decoded.as_bytes(), public_key.as_bytes());
}
#[test]
#[cfg(feature = "serde")]
fn serde_bincode_public_key_matches_from_bytes() {
use bincode;
let expected = PublicKey::from(X25519_BASEPOINT_BYTES);
let decoded: PublicKey = bincode::deserialize(&X25519_BASEPOINT_BYTES).unwrap();
assert_eq!(decoded.as_bytes(), expected.as_bytes());
}
#[test]
#[cfg(feature = "serde")]
fn serde_bincode_static_secret_roundtrip() {
use bincode;
let static_secret = StaticSecret::from([0x24; 32]);
let encoded = bincode::serialize(&static_secret).unwrap();
let decoded: StaticSecret = bincode::deserialize(&encoded).unwrap();
assert_eq!(encoded.len(), 32);
assert_eq!(decoded.to_bytes(), static_secret.to_bytes());
}
#[test]
#[cfg(feature = "serde")]
fn serde_bincode_static_secret_matches_from_bytes() {
use bincode;
let expected = StaticSecret::from([0x24; 32]);
let decoded: StaticSecret = bincode::deserialize(&[0x24; 32]).unwrap();
assert_eq!(decoded.to_bytes(), expected.to_bytes());
}
fn do_rfc7748_ladder_test1(input_scalar: [u8; 32], input_point: [u8; 32], expected: [u8; 32]) {
let result = x25519(input_scalar, input_point);
assert_eq!(result, expected);
}
#[test]
fn rfc7748_ladder_test1_vectorset1() {
let input_scalar: [u8; 32] = [
0xa5, 0x46, 0xe3, 0x6b, 0xf0, 0x52, 0x7c, 0x9d, 0x3b, 0x16, 0x15, 0x4b, 0x82, 0x46, 0x5e,
0xdd, 0x62, 0x14, 0x4c, 0x0a, 0xc1, 0xfc, 0x5a, 0x18, 0x50, 0x6a, 0x22, 0x44, 0xba, 0x44,
0x9a, 0xc4,
];
let input_point: [u8; 32] = [
0xe6, 0xdb, 0x68, 0x67, 0x58, 0x30, 0x30, 0xdb, 0x35, 0x94, 0xc1, 0xa4, 0x24, 0xb1, 0x5f,
0x7c, 0x72, 0x66, 0x24, 0xec, 0x26, 0xb3, 0x35, 0x3b, 0x10, 0xa9, 0x03, 0xa6, 0xd0, 0xab,
0x1c, 0x4c,
];
let expected: [u8; 32] = [
0xc3, 0xda, 0x55, 0x37, 0x9d, 0xe9, 0xc6, 0x90, 0x8e, 0x94, 0xea, 0x4d, 0xf2, 0x8d, 0x08,
0x4f, 0x32, 0xec, 0xcf, 0x03, 0x49, 0x1c, 0x71, 0xf7, 0x54, 0xb4, 0x07, 0x55, 0x77, 0xa2,
0x85, 0x52,
];
do_rfc7748_ladder_test1(input_scalar, input_point, expected);
}
#[test]
fn rfc7748_ladder_test1_vectorset2() {
let input_scalar: [u8; 32] = [
0x4b, 0x66, 0xe9, 0xd4, 0xd1, 0xb4, 0x67, 0x3c, 0x5a, 0xd2, 0x26, 0x91, 0x95, 0x7d, 0x6a,
0xf5, 0xc1, 0x1b, 0x64, 0x21, 0xe0, 0xea, 0x01, 0xd4, 0x2c, 0xa4, 0x16, 0x9e, 0x79, 0x18,
0xba, 0x0d,
];
let input_point: [u8; 32] = [
0xe5, 0x21, 0x0f, 0x12, 0x78, 0x68, 0x11, 0xd3, 0xf4, 0xb7, 0x95, 0x9d, 0x05, 0x38, 0xae,
0x2c, 0x31, 0xdb, 0xe7, 0x10, 0x6f, 0xc0, 0x3c, 0x3e, 0xfc, 0x4c, 0xd5, 0x49, 0xc7, 0x15,
0xa4, 0x93,
];
let expected: [u8; 32] = [
0x95, 0xcb, 0xde, 0x94, 0x76, 0xe8, 0x90, 0x7d, 0x7a, 0xad, 0xe4, 0x5c, 0xb4, 0xb8, 0x73,
0xf8, 0x8b, 0x59, 0x5a, 0x68, 0x79, 0x9f, 0xa1, 0x52, 0xe6, 0xf8, 0xf7, 0x64, 0x7a, 0xac,
0x79, 0x57,
];
do_rfc7748_ladder_test1(input_scalar, input_point, expected);
}
#[test]
#[ignore] // Run only if you want to burn a lot of CPU doing 1,000,000 DH operations
fn rfc7748_ladder_test2() {
use curve25519_dalek::constants::X25519_BASEPOINT;
let mut k: [u8; 32] = X25519_BASEPOINT.0;
let mut u: [u8; 32] = X25519_BASEPOINT.0;
let mut result: [u8; 32];
macro_rules! do_iterations {
($n:expr) => {
for _ in 0..$n {
result = x25519(k, u);
// OBVIOUS THING THAT I'M GOING TO NOTE ANYWAY BECAUSE I'VE
// SEEN PEOPLE DO THIS WITH GOLANG'S STDLIB AND YOU SURE AS
// HELL SHOULDN'T DO HORRIBLY STUPID THINGS LIKE THIS WITH
// MY LIBRARY:
//
// NEVER EVER TREAT SCALARS AS POINTS AND/OR VICE VERSA.
//
// ↓↓ DON'T DO THIS ↓↓
u = k.clone();
k = result;
}
};
}
// After one iteration:
// 422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079
// After 1,000 iterations:
// 684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51
// After 1,000,000 iterations:
// 7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424
do_iterations!(1);
assert_eq!(
k,
[
0x42, 0x2c, 0x8e, 0x7a, 0x62, 0x27, 0xd7, 0xbc, 0xa1, 0x35, 0x0b, 0x3e, 0x2b, 0xb7,
0x27, 0x9f, 0x78, 0x97, 0xb8, 0x7b, 0xb6, 0x85, 0x4b, 0x78, 0x3c, 0x60, 0xe8, 0x03,
0x11, 0xae, 0x30, 0x79,
]
);
do_iterations!(999);
assert_eq!(
k,
[
0x68, 0x4c, 0xf5, 0x9b, 0xa8, 0x33, 0x09, 0x55, 0x28, 0x00, 0xef, 0x56, 0x6f, 0x2f,
0x4d, 0x3c, 0x1c, 0x38, 0x87, 0xc4, 0x93, 0x60, 0xe3, 0x87, 0x5f, 0x2e, 0xb9, 0x4d,
0x99, 0x53, 0x2c, 0x51,
]
);
do_iterations!(999_000);
assert_eq!(
k,
[
0x7c, 0x39, 0x11, 0xe0, 0xab, 0x25, 0x86, 0xfd, 0x86, 0x44, 0x97, 0x29, 0x7e, 0x57,
0x5e, 0x6f, 0x3b, 0xc6, 0x01, 0xc0, 0x88, 0x3c, 0x30, 0xdf, 0x5f, 0x4d, 0xd2, 0xd2,
0x4f, 0x66, 0x54, 0x24,
]
);
}
mod rand_core {
use super::*;
use ::rand_core::OsRng;
#[test]
fn ephemeral_from_rng() {
#[allow(deprecated)]
EphemeralSecret::new(OsRng);
EphemeralSecret::random_from_rng(OsRng);
}
#[test]
#[cfg(feature = "reusable_secrets")]
fn reusable_from_rng() {
#[allow(deprecated)]
ReusableSecret::new(OsRng);
ReusableSecret::random_from_rng(OsRng);
}
#[test]
#[cfg(feature = "static_secrets")]
fn static_from_rng() {
#[allow(deprecated)]
StaticSecret::new(OsRng);
StaticSecret::random_from_rng(OsRng);
}
}
#[cfg(feature = "getrandom")]
mod getrandom {
use super::*;
#[test]
fn ephemeral_random() {
EphemeralSecret::random();
}
#[test]
#[cfg(feature = "reusable_secrets")]
fn reusable_random() {
ReusableSecret::random();
}
#[test]
#[cfg(feature = "static_secrets")]
fn static_random() {
StaticSecret::random();
}
}