mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-06 20:41:14 +00:00
Merge branch 'release/1.0.0'
This commit is contained in:
commit
bddb3c7777
5 changed files with 108 additions and 64 deletions
|
|
@ -2,6 +2,15 @@
|
||||||
|
|
||||||
Entries are listed in reverse chronological order.
|
Entries are listed in reverse chronological order.
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
|
||||||
## 0.6.0
|
## 0.6.0
|
||||||
|
|
||||||
* Updates `rand_core` version to `0.5`.
|
* Updates `rand_core` version to `0.5`.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
[package]
|
[package]
|
||||||
name = "x25519-dalek"
|
name = "x25519-dalek"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
# Be sure to update the version in README.md
|
# Before changing this:
|
||||||
version = "0.6.0"
|
# - update version in README.md
|
||||||
|
# - update html_root_url
|
||||||
|
# - update CHANGELOG
|
||||||
|
version = "1.0.0"
|
||||||
authors = [
|
authors = [
|
||||||
"Isis Lovecruft <isis@patternsinthevoid.net>",
|
"Isis Lovecruft <isis@patternsinthevoid.net>",
|
||||||
"DebugSteven <debugsteven@gmail.com>",
|
"DebugSteven <debugsteven@gmail.com>",
|
||||||
|
|
@ -39,7 +42,7 @@ zeroize = { version = "1", default-features = false, features = ["zeroize_derive
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
bincode = "1"
|
bincode = "1"
|
||||||
criterion = "0.2"
|
criterion = "0.3.0"
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
name = "x25519"
|
name = "x25519"
|
||||||
|
|
|
||||||
63
README.md
63
README.md
|
|
@ -31,45 +31,60 @@ the rest of the afternoon nomming some yummy pie!
|
||||||
First, Alice uses `EphemeralSecret::new()` and then
|
First, Alice uses `EphemeralSecret::new()` and then
|
||||||
`PublicKey::from()` to produce her secret and public keys:
|
`PublicKey::from()` to produce her secret and public keys:
|
||||||
|
|
||||||
```rust,ignore
|
```rust
|
||||||
extern crate rand_os;
|
use rand_core::OsRng;
|
||||||
extern crate x25519_dalek;
|
use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||||
|
|
||||||
use rand_os::OsRng;
|
let alice_secret = EphemeralSecret::new(OsRng);
|
||||||
|
let alice_public = PublicKey::from(&alice_secret);
|
||||||
use x25519_dalek::EphemeralSecret;
|
|
||||||
use x25519_dalek::PublicKey;
|
|
||||||
|
|
||||||
let mut alice_csprng = OsRng::new().unwrap();
|
|
||||||
let alice_secret = EphemeralSecret::new(&mut alice_csprng);
|
|
||||||
let alice_public = PublicKey::from(&alice_secret);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Bob does the same:
|
Bob does the same:
|
||||||
|
|
||||||
```rust,ignore
|
```rust
|
||||||
let mut bob_csprng = OsRng::new().unwrap();
|
# use rand_core::OsRng;
|
||||||
let bob_secret = EphemeralSecret::new(&mut bob_csprng);
|
# use x25519_dalek::{EphemeralSecret, PublicKey};
|
||||||
let bob_public = PublicKey::from(&bob_secret);
|
let bob_secret = EphemeralSecret::new(OsRng);
|
||||||
|
let bob_public = PublicKey::from(&bob_secret);
|
||||||
```
|
```
|
||||||
|
|
||||||
Alice meows across the room, telling `alice_public` to Bob, and Bob
|
Alice meows across the room, telling `alice_public` to Bob, and Bob
|
||||||
loudly meows `bob_public` back to Alice. Alice now computes her
|
loudly meows `bob_public` back to Alice. Alice now computes her
|
||||||
shared secret with Bob by doing:
|
shared secret with Bob by doing:
|
||||||
|
|
||||||
```rust,ignore
|
```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 alice_shared_secret = alice_secret.diffie_hellman(&bob_public);
|
||||||
```
|
```
|
||||||
|
|
||||||
Similarly, Bob computes a shared secret by doing:
|
Similarly, Bob computes a shared secret by doing:
|
||||||
|
|
||||||
```rust,ignore
|
```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);
|
let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);
|
||||||
```
|
```
|
||||||
|
|
||||||
These secrets are the same:
|
These secrets are the same:
|
||||||
|
|
||||||
```rust,ignore
|
```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());
|
assert_eq!(alice_shared_secret.as_bytes(), bob_shared_secret.as_bytes());
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -86,8 +101,8 @@ and load a long-term secret key.
|
||||||
To install, add the following to your project's `Cargo.toml`:
|
To install, add the following to your project's `Cargo.toml`:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
[dependencies.x25519-dalek]
|
[dependencies]
|
||||||
version = "0.6"
|
x25519-dalek = "1"
|
||||||
```
|
```
|
||||||
|
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
@ -105,3 +120,11 @@ attempt to prevent software side-channels.
|
||||||
copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg))
|
copyright © Amy Wibowo ([@sailorhg](https://twitter.com/sailorhg))
|
||||||
|
|
||||||
[rfc7748]: https://tools.ietf.org/html/rfc7748
|
[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
|
||||||
|
|
||||||
|
[crypto_box]: https://github.com/RustCrypto/AEADs/tree/master/crypto_box
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@
|
||||||
#![cfg_attr(feature = "nightly", deny(missing_docs))]
|
#![cfg_attr(feature = "nightly", deny(missing_docs))]
|
||||||
#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
|
#![cfg_attr(feature = "nightly", doc(include = "../README.md"))]
|
||||||
#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")]
|
#![doc(html_logo_url = "https://doc.dalek.rs/assets/dalek-logo-clear.png")]
|
||||||
|
#![doc(html_root_url = "https://docs.rs/x25519-dalek/1.0.0")]
|
||||||
|
|
||||||
//! Note that docs will only build on nightly Rust until
|
//! Note that docs will only build on nightly Rust until
|
||||||
//! `feature(external_doc)` is stabilized.
|
//! `feature(external_doc)` is stabilized.
|
||||||
|
|
|
||||||
|
|
@ -23,8 +23,7 @@ use rand_core::RngCore;
|
||||||
|
|
||||||
use zeroize::Zeroize;
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
/// A `PublicKey` is the corresponding public key converted from
|
/// A Diffie-Hellman public key, corresponding to an [`EphemeralSecret`] or [`StaticSecret`] key.
|
||||||
/// an `EphemeralSecret` or a `StaticSecret` key.
|
|
||||||
#[cfg_attr(feature = "serde", serde(crate = "our_serde"))]
|
#[cfg_attr(feature = "serde", serde(crate = "our_serde"))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
|
|
@ -41,6 +40,12 @@ impl From<[u8; 32]> for PublicKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PublicKey {
|
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.
|
/// View this public key as a byte array.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
pub fn as_bytes(&self) -> &[u8; 32] {
|
||||||
|
|
@ -48,24 +53,27 @@ impl PublicKey {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `EphemeralSecret` is a short lived Diffie-Hellman secret key
|
/// A short-lived Diffie-Hellman secret key that can only be used to compute a single
|
||||||
/// used to create a `SharedSecret` when given their `PublicKey`.
|
/// [`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 by [`EphemeralSecret::new`] and the compiler statically checks
|
||||||
|
/// that the resulting secret is used at most once.
|
||||||
#[derive(Zeroize)]
|
#[derive(Zeroize)]
|
||||||
#[zeroize(drop)]
|
#[zeroize(drop)]
|
||||||
pub struct EphemeralSecret(pub(crate) Scalar);
|
pub struct EphemeralSecret(pub(crate) Scalar);
|
||||||
|
|
||||||
impl EphemeralSecret {
|
impl EphemeralSecret {
|
||||||
/// Perform a Diffie-Hellman key agreement between `self` and
|
/// Perform a Diffie-Hellman key agreement between `self` and
|
||||||
/// `their_public` key to produce a `SharedSecret`.
|
/// `their_public` key to produce a [`SharedSecret`].
|
||||||
pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
|
pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
|
||||||
SharedSecret(self.0 * their_public.0)
|
SharedSecret(self.0 * their_public.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate an x25519 `EphemeralSecret` key.
|
/// Generate an x25519 [`EphemeralSecret`] key.
|
||||||
pub fn new<T>(csprng: &mut T) -> Self
|
pub fn new<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
|
||||||
where
|
|
||||||
T: RngCore + CryptoRng,
|
|
||||||
{
|
|
||||||
let mut bytes = [0u8; 32];
|
let mut bytes = [0u8; 32];
|
||||||
|
|
||||||
csprng.fill_bytes(&mut bytes);
|
csprng.fill_bytes(&mut bytes);
|
||||||
|
|
@ -75,16 +83,27 @@ impl EphemeralSecret {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a EphemeralSecret> for PublicKey {
|
impl<'a> From<&'a EphemeralSecret> for PublicKey {
|
||||||
/// Given an x25519 `EphemeralSecret` key, compute its corresponding
|
/// Given an x25519 [`EphemeralSecret`] key, compute its corresponding [`PublicKey`].
|
||||||
/// `PublicKey` key.
|
|
||||||
fn from(secret: &'a EphemeralSecret) -> PublicKey {
|
fn from(secret: &'a EphemeralSecret) -> PublicKey {
|
||||||
PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery())
|
PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `StaticSecret` is a static Diffie-Hellman secret key that
|
/// A Diffie-Hellman secret key that can be used to compute multiple [`SharedSecret`]s.
|
||||||
/// can be saved and loaded to create a `SharedSecret` when given
|
///
|
||||||
/// their `PublicKey`.
|
/// 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*).
|
||||||
|
///
|
||||||
|
/// Some protocols, such as Noise, already handle the static/ephemeral distinction, so the
|
||||||
|
/// additional guarantees provided by [`EphemeralSecret`] are not helpful or would cause duplicate
|
||||||
|
/// code paths. In this case, it may be useful to
|
||||||
|
/// ```rust,ignore
|
||||||
|
/// use x25519_dalek::StaticSecret as SecretKey;
|
||||||
|
/// ```
|
||||||
|
/// since the only difference between the two is that [`StaticSecret`] does not enforce at
|
||||||
|
/// compile-time that the key is only used once.
|
||||||
#[cfg_attr(feature = "serde", serde(crate = "our_serde"))]
|
#[cfg_attr(feature = "serde", serde(crate = "our_serde"))]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "serde",
|
feature = "serde",
|
||||||
|
|
@ -103,11 +122,8 @@ impl StaticSecret {
|
||||||
SharedSecret(&self.0 * their_public.0)
|
SharedSecret(&self.0 * their_public.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate a x25519 `StaticSecret` key.
|
/// Generate an x25519 key.
|
||||||
pub fn new<T>(csprng: &mut T) -> Self
|
pub fn new<T: RngCore + CryptoRng>(mut csprng: T) -> Self {
|
||||||
where
|
|
||||||
T: RngCore + CryptoRng,
|
|
||||||
{
|
|
||||||
let mut bytes = [0u8; 32];
|
let mut bytes = [0u8; 32];
|
||||||
|
|
||||||
csprng.fill_bytes(&mut bytes);
|
csprng.fill_bytes(&mut bytes);
|
||||||
|
|
@ -115,34 +131,41 @@ impl StaticSecret {
|
||||||
StaticSecret(clamp_scalar(bytes))
|
StaticSecret(clamp_scalar(bytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save a x25519 `StaticSecret` key's bytes.
|
/// Extract this key's bytes for serialization.
|
||||||
pub fn to_bytes(&self) -> [u8; 32] {
|
pub fn to_bytes(&self) -> [u8; 32] {
|
||||||
self.0.to_bytes()
|
self.0.to_bytes()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<[u8; 32]> for StaticSecret {
|
impl From<[u8; 32]> for StaticSecret {
|
||||||
/// Load a `StaticSecret` from a byte array.
|
/// Load a secret key from a byte array.
|
||||||
fn from(bytes: [u8; 32]) -> StaticSecret {
|
fn from(bytes: [u8; 32]) -> StaticSecret {
|
||||||
StaticSecret(clamp_scalar(bytes))
|
StaticSecret(clamp_scalar(bytes))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a StaticSecret> for PublicKey {
|
impl<'a> From<&'a StaticSecret> for PublicKey {
|
||||||
/// Given an x25519 `StaticSecret` key, compute its corresponding
|
/// Given an x25519 [`StaticSecret`] key, compute its corresponding [`PublicKey`].
|
||||||
/// `PublicKey` key.
|
|
||||||
fn from(secret: &'a StaticSecret) -> PublicKey {
|
fn from(secret: &'a StaticSecret) -> PublicKey {
|
||||||
PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery())
|
PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `SharedSecret` is a Diffie-Hellman shared secret that’s generated
|
/// The result of a Diffie-Hellman key exchange.
|
||||||
/// from your `EphemeralSecret` or `StaticSecret` and their `PublicKey`.
|
///
|
||||||
|
/// Each party computes this using their [`EphemeralSecret`] or [`StaticSecret`] and their
|
||||||
|
/// counterparty's [`PublicKey`].
|
||||||
#[derive(Zeroize)]
|
#[derive(Zeroize)]
|
||||||
#[zeroize(drop)]
|
#[zeroize(drop)]
|
||||||
pub struct SharedSecret(pub(crate) MontgomeryPoint);
|
pub struct SharedSecret(pub(crate) MontgomeryPoint);
|
||||||
|
|
||||||
impl SharedSecret {
|
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.
|
/// View this shared secret key as a byte array.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_bytes(&self) -> &[u8; 32] {
|
pub fn as_bytes(&self) -> &[u8; 32] {
|
||||||
|
|
@ -205,21 +228,6 @@ mod test {
|
||||||
|
|
||||||
use rand_core::OsRng;
|
use rand_core::OsRng;
|
||||||
|
|
||||||
// This was previously a doctest but it got moved to the README to
|
|
||||||
// avoid duplication where it then wasn't being run, so now it
|
|
||||||
// lives here.
|
|
||||||
#[test]
|
|
||||||
fn alice_and_bob() {
|
|
||||||
let alice_secret = EphemeralSecret::new(&mut OsRng);
|
|
||||||
let alice_public = PublicKey::from(&alice_secret);
|
|
||||||
let bob_secret = EphemeralSecret::new(&mut 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());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn byte_basepoint_matches_edwards_scalar_mul() {
|
fn byte_basepoint_matches_edwards_scalar_mul() {
|
||||||
let mut scalar_bytes = [0x37; 32];
|
let mut scalar_bytes = [0x37; 32];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue