diff --git a/Cargo.toml b/Cargo.toml index e37ea8d..107c46e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ exclude = [ ".gitignore", "TESTVECTORS", "res/*" ] travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"} [dependencies.curve25519-dalek] -version = "0.18" +version = "0.19" default-features = false [dependencies.rand] @@ -42,6 +42,9 @@ optional = true version = "^0.1.1" default-features = false +[dependencies.clear_on_drop] +version = "0.2" + [dev-dependencies] hex = "^0.3" sha2 = "^0.7" @@ -56,7 +59,7 @@ harness = false default = ["std", "u64_backend"] # We don't add "rand/std" here because it would enable a bunch of Fuchsia dependencies. std = ["curve25519-dalek/std"] -nightly = ["curve25519-dalek/nightly", "rand/nightly"] +nightly = ["curve25519-dalek/nightly", "rand/nightly", "clear_on_drop/nightly"] asm = ["sha2/asm"] yolocrypto = ["curve25519-dalek/yolocrypto"] u64_backend = ["curve25519-dalek/u64_backend"] diff --git a/src/ed25519.rs b/src/ed25519.rs index f1f0d0b..54bec6b 100644 --- a/src/ed25519.rs +++ b/src/ed25519.rs @@ -10,6 +10,7 @@ //! A Rust implementation of ed25519 EdDSA key generation, signing, and //! verification. +use core::default::Default; use core::fmt::{Debug}; use rand::CryptoRng; @@ -27,6 +28,8 @@ use serde::de::Visitor; #[cfg(feature = "sha2")] use sha2::Sha512; +use clear_on_drop::clear::Clear; + use digest::Digest; use generic_array::typenum::U64; @@ -165,6 +168,7 @@ impl<'d> Deserialize<'d> for Signature { /// An EdDSA secret key. #[repr(C)] +#[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct SecretKey(pub (crate) [u8; SECRET_KEY_LENGTH]); impl Debug for SecretKey { @@ -173,6 +177,13 @@ impl Debug for SecretKey { } } +/// Overwrite secret key material with null bytes when it goes out of scope. +impl Drop for SecretKey { + fn drop(&mut self) { + self.0.clear(); + } +} + impl SecretKey { /// Expand this `SecretKey` into an `ExpandedSecretKey`. pub fn expand(&self) -> ExpandedSecretKey where D: Digest + Default { @@ -370,11 +381,20 @@ impl<'d> Deserialize<'d> for SecretKey { // better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on // "generalised EdDSA" and "VXEdDSA". #[repr(C)] +#[derive(Default)] // we derive Default in order to use the clear() method in Drop pub struct ExpandedSecretKey { pub (crate) key: Scalar, pub (crate) nonce: [u8; 32], } +/// Overwrite secret key material with null bytes when it goes out of scope. +impl Drop for ExpandedSecretKey { + fn drop(&mut self) { + self.key.clear(); + self.nonce.clear(); + } +} + #[cfg(feature = "sha2")] impl<'a> From<&'a SecretKey> for ExpandedSecretKey { /// Construct an `ExpandedSecretKey` from a `SecretKey`. @@ -683,7 +703,7 @@ impl<'d> Deserialize<'d> for ExpandedSecretKey { } /// An ed25519 public key. -#[derive(Copy, Clone, Eq, PartialEq)] +#[derive(Copy, Clone, Default, Eq, PartialEq)] #[repr(C)] pub struct PublicKey(pub (crate) CompressedEdwardsY); @@ -894,7 +914,7 @@ impl<'d> Deserialize<'d> for PublicKey { } /// An ed25519 keypair. -#[derive(Debug)] +#[derive(Debug, Default)] // we derive Default in order to use the clear() method in Drop #[repr(C)] pub struct Keypair { /// The secret half of this keypair. @@ -1416,6 +1436,24 @@ mod test { 175, 002, 026, 104, 247, 007, 081, 026, ])))) } + #[test] + fn keypair_clear_on_drop() { + let mut keypair: Keypair = Keypair::from_bytes(&[15u8; KEYPAIR_LENGTH][..]).unwrap(); + + keypair.clear(); + + fn as_bytes(x: &T) -> &[u8] { + use core::mem; + use core::slice; + + unsafe { + slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) + } + } + + assert!(!as_bytes(&keypair).contains(&0x15)); + } + #[cfg(all(test, feature = "serde"))] use bincode::{serialize, deserialize, Infinite}; diff --git a/src/lib.rs b/src/lib.rs index 401c4ea..f815da8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,6 +264,7 @@ extern crate generic_array; extern crate digest; extern crate failure; extern crate rand; +extern crate clear_on_drop; #[cfg(any(feature = "std", test))] #[macro_use]