Merge pull request #35 from DebugSteven/staticsecret

Static secret keys
This commit is contained in:
Henry de Valence 2019-02-16 13:38:47 -08:00 committed by GitHub
commit 724c929130
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 157 additions and 40 deletions

View file

@ -5,6 +5,7 @@ rust:
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'

View file

@ -6,7 +6,7 @@ with curve operations provided by
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 ephemeral Diffie-Hellman.
well as a higher-level Rust API for static and ephemeral Diffie-Hellman.
## 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!
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
extern crate x25519_dalek;
extern crate rand_os;
use x25519_dalek::EphemeralPublic;
use x25519_dalek::EphemeralSecret;
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 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:
```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 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
loudly meows `bob_public` back to Alice. Alice now computes her
shared secret with Bob by doing:
```rust,ignore
use x25519_dalek::EphemeralPublic;
use x25519_dalek::EphemeralSecret;
let shared_secret = EphemeralSecret::diffie_hellman(alice_secret, &bob_public);
```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);
# }
```
Similarly, Bob computes the same shared secret by doing:
Similarly, Bob computes a shared secret by doing:
```rust,ignore
let shared_secret = EphemeralSecret::diffie_hellman(bob_secret, &alice_public);
```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 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
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`:

View file

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

View file

@ -23,25 +23,27 @@ use curve25519_dalek::scalar::Scalar;
use rand_core::RngCore;
use rand_core::CryptoRng;
/// A DH ephemeral public key.
pub struct EphemeralPublic(pub (crate) MontgomeryPoint);
/// A `PublicKey` is the corresponding public key converted from
/// an `EphemeralSecret` or a `StaticSecret` key.
pub struct PublicKey(pub (crate) MontgomeryPoint);
impl From<[u8; 32]> for EphemeralPublic {
/// Given a byte array, construct an x25519 `EphemeralPublic` key
fn from(bytes: [u8; 32]) -> EphemeralPublic {
EphemeralPublic(MontgomeryPoint(bytes))
impl From<[u8; 32]> for PublicKey {
/// Given a byte array, construct a x25519 `PublicKey`.
fn from(bytes: [u8; 32]) -> PublicKey {
PublicKey(MontgomeryPoint(bytes))
}
}
impl EphemeralPublic {
/// View this ephemeral public key as a byte array.
impl PublicKey {
/// View this public key as a byte array.
#[inline]
pub fn as_bytes(&self) -> &[u8; 32] {
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);
/// Overwrite ephemeral secret key material with null bytes when it goes out of scope.
@ -52,10 +54,9 @@ impl Drop for EphemeralSecret {
}
impl EphemeralSecret {
/// Utility function to make it easier to call `x25519()` with
/// an ephemeral secret key and montegomery point as input and
/// a shared secret as the output.
pub fn diffie_hellman(self, their_public: &EphemeralPublic) -> SharedSecret {
/// 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)
}
@ -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
/// `EphemeralPublic` key.
fn from(secret: &'a EphemeralSecret) -> EphemeralPublic {
EphemeralPublic((&ED25519_BASEPOINT_TABLE * &secret.0).to_montgomery())
/// `PublicKey` key.
fn from(secret: &'a EphemeralSecret) -> PublicKey {
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);
/// 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.
///
/// 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] {
(clamp_scalar(k) * MontgomeryPoint(u)).to_bytes()
}
/// The X25519 basepoint, for use with the bare, byte-oriented x25519
/// 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] = [
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,
];