mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-07 20:50:39 +00:00
Merge pull request #35 from DebugSteven/staticsecret
Static secret keys
This commit is contained in:
commit
724c929130
4 changed files with 157 additions and 40 deletions
|
|
@ -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'
|
||||||
|
|
|
||||||
95
README.md
95
README.md
|
|
@ -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`:
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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 that’s 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,
|
||||||
];
|
];
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue