Merge branch 'release/0.5.0'

This commit is contained in:
Henry de Valence 2019-02-16 13:47:14 -08:00
commit 34676d3360
6 changed files with 166 additions and 41 deletions

View file

@ -5,6 +5,7 @@ rust:
env: env:
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='default' - TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='default'
- TEST_COMMAND=test EXTRA_FLAGS='' FEATURES='nightly'
- TEST_COMMAND=bench EXTRA_FLAGS='' FEATURES='default' - 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='u32_backend nightly'
- TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u64_backend nightly' - TEST_COMMAND=build EXTRA_FLAGS='--no-default-features' FEATURES='u64_backend nightly'

8
CHANGELOG.md Normal file
View file

@ -0,0 +1,8 @@
# Changelog
Entries are listed in reverse chronological order.
## 0.5.0
* Adds support for static and ephemeral keys.

View file

@ -1,6 +1,6 @@
[package] [package]
name = "x25519-dalek" name = "x25519-dalek"
version = "0.4.5" version = "0.5.0"
authors = [ authors = [
"Isis Lovecruft <isis@patternsinthevoid.net>", "Isis Lovecruft <isis@patternsinthevoid.net>",
"DebugSteven <debugsteven@gmail.com>", "DebugSteven <debugsteven@gmail.com>",

View file

@ -6,7 +6,7 @@ with curve operations provided by
This crate provides two levels of API: a bare byte-oriented `x25519` This crate provides two levels of API: a bare byte-oriented `x25519`
function which matches the function specified in [RFC7748][rfc7748], as function which matches the function specified in [RFC7748][rfc7748], as
well as a higher-level Rust API for ephemeral Diffie-Hellman. well as a higher-level Rust API for static and ephemeral Diffie-Hellman.
## Examples ## Examples
@ -29,50 +29,111 @@ kittens will be able to secretly organise to find their mittens, and then spend
the rest of the afternoon nomming some yummy pie! the rest of the afternoon nomming some yummy pie!
First, Alice uses `EphemeralSecret::new()` and then First, Alice uses `EphemeralSecret::new()` and then
`EphemeralPublic::from()` to produce her secret and public keys: `PublicKey::from()` to produce her secret and public keys:
```rust ```rust
extern crate x25519_dalek;
extern crate rand_os; extern crate rand_os;
use x25519_dalek::EphemeralPublic;
use x25519_dalek::EphemeralSecret;
use rand_os::OsRng; use rand_os::OsRng;
extern crate x25519_dalek;
use x25519_dalek::EphemeralSecret;
use x25519_dalek::PublicKey;
# fn main() {
let mut alice_csprng = OsRng::new().unwrap(); let mut alice_csprng = OsRng::new().unwrap();
let alice_secret = EphemeralSecret::new(&mut alice_csprng); let alice_secret = EphemeralSecret::new(&mut alice_csprng);
let alice_public = EphemeralPublic::from(&alice_secret); let alice_public = PublicKey::from(&alice_secret);
# }
``` ```
Bob does the same: Bob does the same:
```rust,ignore ```rust
# extern crate rand_os;
# use rand_os::OsRng;
#
# extern crate x25519_dalek;
# use x25519_dalek::EphemeralSecret;
# use x25519_dalek::PublicKey;
# fn main() {
let mut bob_csprng = OsRng::new().unwrap(); let mut bob_csprng = OsRng::new().unwrap();
let bob_secret = EphemeralSecret::new(&mut bob_csprng); let bob_secret = EphemeralSecret::new(&mut bob_csprng);
let bob_public = EphemeralPublic::from(&bob_secret); 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 x25519_dalek::EphemeralPublic; # extern crate rand_os;
use x25519_dalek::EphemeralSecret; # use rand_os::OsRng;
#
let shared_secret = EphemeralSecret::diffie_hellman(alice_secret, &bob_public); # extern crate x25519_dalek;
# use x25519_dalek::EphemeralSecret;
# use x25519_dalek::PublicKey;
#
# fn main() {
# let mut csprng = OsRng::new().unwrap();
# let alice_secret = EphemeralSecret::new(&mut csprng);
# let alice_public = PublicKey::from(&alice_secret);
# let bob_secret = EphemeralSecret::new(&mut csprng);
# let bob_public = PublicKey::from(&bob_secret);
let alice_shared_secret = alice_secret.diffie_hellman(&bob_public);
# }
``` ```
Similarly, Bob computes the same shared secret by doing: Similarly, Bob computes a shared secret by doing:
```rust,ignore ```rust
let shared_secret = EphemeralSecret::diffie_hellman(bob_secret, &alice_public); # extern crate rand_os;
# use rand_os::OsRng;
#
# extern crate x25519_dalek;
# use x25519_dalek::EphemeralSecret;
# use x25519_dalek::PublicKey;
#
# fn main() {
# let mut csprng = OsRng::new().unwrap();
# let alice_secret = EphemeralSecret::new(&mut csprng);
# let alice_public = PublicKey::from(&alice_secret);
# let bob_secret = EphemeralSecret::new(&mut csprng);
# let bob_public = PublicKey::from(&bob_secret);
let bob_shared_secret = bob_secret.diffie_hellman(&alice_public);
# }
```
These secrets are the same:
```rust
# extern crate rand_os;
# use rand_os::OsRng;
#
# extern crate x25519_dalek;
# use x25519_dalek::EphemeralSecret;
# use x25519_dalek::PublicKey;
#
# fn main() {
# let mut csprng = OsRng::new().unwrap();
# let alice_secret = EphemeralSecret::new(&mut csprng);
# let alice_public = PublicKey::from(&alice_secret);
# let bob_secret = EphemeralSecret::new(&mut csprng);
# 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 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 meows, for example, by using it to generate a key and nonce for an
authenticated-encryption cipher. 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 # Installation
To install, add the following to your project's `Cargo.toml`: To install, add the following to your project's `Cargo.toml`:

View file

@ -23,13 +23,13 @@ use curve25519_dalek::montgomery::MontgomeryPoint;
use rand_os::OsRng; use rand_os::OsRng;
use x25519_dalek::EphemeralPublic; use x25519_dalek::PublicKey;
use x25519_dalek::EphemeralSecret; use x25519_dalek::EphemeralSecret;
fn bench_diffie_hellman(c: &mut Criterion) { fn bench_diffie_hellman(c: &mut Criterion) {
let mut csprng: OsRng = OsRng::new().unwrap(); let mut csprng: OsRng = OsRng::new().unwrap();
let bob_secret: EphemeralSecret = EphemeralSecret::new(&mut csprng); let bob_secret = EphemeralSecret::new(&mut csprng);
let bob_public: EphemeralPublic = EphemeralPublic::from(&bob_secret); let bob_public = PublicKey::from(&bob_secret);
c.bench_function("diffie_hellman", move |b| { c.bench_function("diffie_hellman", move |b| {
b.iter_with_setup( b.iter_with_setup(

View file

@ -23,25 +23,27 @@ use curve25519_dalek::scalar::Scalar;
use rand_core::RngCore; use rand_core::RngCore;
use rand_core::CryptoRng; use rand_core::CryptoRng;
/// A DH ephemeral public key. /// A `PublicKey` is the corresponding public key converted from
pub struct EphemeralPublic(pub (crate) MontgomeryPoint); /// an `EphemeralSecret` or a `StaticSecret` key.
pub struct PublicKey(pub (crate) MontgomeryPoint);
impl From<[u8; 32]> for EphemeralPublic { impl From<[u8; 32]> for PublicKey {
/// Given a byte array, construct an x25519 `EphemeralPublic` key /// Given a byte array, construct a x25519 `PublicKey`.
fn from(bytes: [u8; 32]) -> EphemeralPublic { fn from(bytes: [u8; 32]) -> PublicKey {
EphemeralPublic(MontgomeryPoint(bytes)) PublicKey(MontgomeryPoint(bytes))
} }
} }
impl EphemeralPublic { impl PublicKey {
/// View this ephemeral 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] {
self.0.as_bytes() self.0.as_bytes()
} }
} }
/// A DH ephemeral secret key. /// A `EphemeralSecret` is a short lived Diffie-Hellman secret key
/// used to create a `SharedSecret` when given their `PublicKey`.
pub struct EphemeralSecret(pub (crate) Scalar); pub struct EphemeralSecret(pub (crate) Scalar);
/// Overwrite ephemeral secret key material with null bytes when it goes out of scope. /// Overwrite ephemeral secret key material with null bytes when it goes out of scope.
@ -52,10 +54,9 @@ impl Drop for EphemeralSecret {
} }
impl EphemeralSecret { impl EphemeralSecret {
/// Utility function to make it easier to call `x25519()` with /// Perform a Diffie-Hellman key agreement between `self` and
/// an ephemeral secret key and montegomery point as input and /// `their_public` key to produce a `SharedSecret`.
/// a shared secret as the output. pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
pub fn diffie_hellman(self, their_public: &EphemeralPublic) -> SharedSecret {
SharedSecret(self.0 * their_public.0) SharedSecret(self.0 * their_public.0)
} }
@ -72,16 +73,70 @@ impl EphemeralSecret {
} }
impl<'a> From<&'a EphemeralSecret> for EphemeralPublic { impl<'a> From<&'a EphemeralSecret> for PublicKey {
/// Given an x25519 `EphemeralSecret` key, compute its corresponding /// Given an x25519 `EphemeralSecret` key, compute its corresponding
/// `EphemeralPublic` key. /// `PublicKey` key.
fn from(secret: &'a EphemeralSecret) -> EphemeralPublic { fn from(secret: &'a EphemeralSecret) -> PublicKey {
EphemeralPublic((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery()) PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery())
} }
} }
/// A DH SharedSecret /// A `StaticSecret` is a static Diffie-Hellman secret key that
/// can be saved and loaded to create a `SharedSecret` when given
/// their `PublicKey`.
pub struct StaticSecret(pub (crate) Scalar);
/// Overwrite static secret key material with null bytes when it goes out of scope.
impl Drop for StaticSecret {
fn drop(&mut self) {
self.0.clear();
}
}
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(&self.0 * their_public.0)
}
/// Generate a x25519 `StaticSecret` key.
pub fn new<T>(csprng: &mut T) -> Self
where T: RngCore + CryptoRng
{
let mut bytes = [0u8; 32];
csprng.fill_bytes(&mut bytes);
StaticSecret(clamp_scalar(bytes))
}
/// Save a x25519 `StaticSecret` key's bytes.
pub fn to_bytes(&self) -> [u8; 32] {
self.0.to_bytes()
}
}
impl From<[u8; 32]> for StaticSecret {
/// Load a `StaticSecret` from a byte array.
fn from(bytes: [u8; 32]) -> StaticSecret {
StaticSecret(clamp_scalar(bytes))
}
}
impl<'a> From<&'a StaticSecret> for PublicKey {
/// Given an x25519 `StaticSecret` key, compute its corresponding
/// `PublicKey` key.
fn from(secret: &'a StaticSecret) -> PublicKey {
PublicKey((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery())
}
}
/// A `SharedSecret` is a Diffie-Hellman shared secret thats generated
/// from your `EphemeralSecret` or `StaticSecret` and their `PublicKey`.
pub struct SharedSecret(pub (crate) MontgomeryPoint); pub struct SharedSecret(pub (crate) MontgomeryPoint);
/// Overwrite shared secret material with null bytes when it goes out of scope. /// Overwrite shared secret material with null bytes when it goes out of scope.
@ -120,14 +175,14 @@ fn clamp_scalar(scalar: [u8; 32]) -> Scalar {
/// The bare, byte-oriented x25519 function, exactly as specified in RFC7748. /// The bare, byte-oriented x25519 function, exactly as specified in RFC7748.
/// ///
/// This can be used with [`X25519_BASEPOINT_BYTES`] for people who /// This can be used with [`X25519_BASEPOINT_BYTES`] for people who
/// cannot use the better, safer, and faster ephemeral DH API. /// cannot use the better, safer, and faster DH API.
pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] { pub fn x25519(k: [u8; 32], u: [u8; 32]) -> [u8; 32] {
(clamp_scalar(k) * MontgomeryPoint(u)).to_bytes() (clamp_scalar(k) * MontgomeryPoint(u)).to_bytes()
} }
/// The X25519 basepoint, for use with the bare, byte-oriented x25519 /// The X25519 basepoint, for use with the bare, byte-oriented x25519
/// function. This is provided for people who cannot use the typed /// function. This is provided for people who cannot use the typed
/// ephemeral DH API for some reason. /// DH API for some reason.
pub const X25519_BASEPOINT_BYTES: [u8; 32] = [ 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, 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,
]; ];