mirror of
https://github.com/saymrwulf/curve25519-dalek-source.git
synced 2026-09-05 20:30:57 +00:00
Switch to using zeroize rather than clear_on_drop.
This commit is contained in:
parent
7db743bd43
commit
8ca3be99e9
4 changed files with 37 additions and 47 deletions
|
|
@ -22,13 +22,13 @@ travis-ci = { repository = "dalek-cryptography/ed25519-dalek", branch = "master"
|
||||||
features = ["nightly", "batch"]
|
features = ["nightly", "batch"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clear_on_drop = { version = "0.2" }
|
|
||||||
curve25519-dalek = { version = "2", default-features = false }
|
curve25519-dalek = { version = "2", default-features = false }
|
||||||
merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" }
|
merlin = { version = "1", default-features = false, optional = true, git = "https://github.com/isislovecruft/merlin", branch = "develop" }
|
||||||
rand = { version = "0.7", default-features = false, optional = true }
|
rand = { version = "0.7", default-features = false, optional = true }
|
||||||
rand_core = { version = "0.5", default-features = false, optional = true }
|
rand_core = { version = "0.5", default-features = false, optional = true }
|
||||||
serde = { version = "1.0", optional = true }
|
serde = { version = "1.0", optional = true }
|
||||||
sha2 = { version = "0.8", default-features = false }
|
sha2 = { version = "0.8", default-features = false }
|
||||||
|
zeroize = { version = "1", default-features = false, features = ["zeroize_derive"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
hex = "^0.4"
|
hex = "^0.4"
|
||||||
|
|
@ -46,8 +46,8 @@ harness = false
|
||||||
[features]
|
[features]
|
||||||
default = ["std", "u64_backend"]
|
default = ["std", "u64_backend"]
|
||||||
std = ["curve25519-dalek/std", "sha2/std", "rand/std"]
|
std = ["curve25519-dalek/std", "sha2/std", "rand/std"]
|
||||||
alloc = ["curve25519-dalek/alloc", "rand/alloc"]
|
alloc = ["curve25519-dalek/alloc", "rand/alloc", "zeroize/alloc"]
|
||||||
nightly = ["curve25519-dalek/nightly", "clear_on_drop/nightly", "rand/nightly"]
|
nightly = ["curve25519-dalek/nightly", "rand/nightly"]
|
||||||
batch = ["merlin", "rand"]
|
batch = ["merlin", "rand"]
|
||||||
# This feature enables deterministic batch verification.
|
# This feature enables deterministic batch verification.
|
||||||
batch_deterministic = ["merlin", "rand", "rand_core"]
|
batch_deterministic = ["merlin", "rand", "rand_core"]
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ pub use crate::secret::*;
|
||||||
pub use crate::signature::*;
|
pub use crate::signature::*;
|
||||||
|
|
||||||
/// An ed25519 keypair.
|
/// An ed25519 keypair.
|
||||||
#[derive(Debug, Default)] // we derive Default in order to use the clear() method in Drop
|
#[derive(Debug)]
|
||||||
pub struct Keypair {
|
pub struct Keypair {
|
||||||
/// The secret half of this keypair.
|
/// The secret half of this keypair.
|
||||||
pub secret: SecretKey,
|
pub secret: SecretKey,
|
||||||
|
|
@ -444,26 +444,3 @@ impl<'d> Deserialize<'d> for Keypair {
|
||||||
deserializer.deserialize_bytes(KeypairVisitor)
|
deserializer.deserialize_bytes(KeypairVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
use clear_on_drop::clear::Clear;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn keypair_clear_on_drop() {
|
|
||||||
let mut keypair: Keypair = Keypair::from_bytes(&[1u8; KEYPAIR_LENGTH][..]).unwrap();
|
|
||||||
|
|
||||||
keypair.clear();
|
|
||||||
|
|
||||||
fn as_bytes<T>(x: &T) -> &[u8] {
|
|
||||||
use std::mem;
|
|
||||||
use std::slice;
|
|
||||||
|
|
||||||
unsafe { slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) }
|
|
||||||
}
|
|
||||||
|
|
||||||
assert!(!as_bytes(&keypair).contains(&0x15));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -239,7 +239,6 @@ extern crate std;
|
||||||
|
|
||||||
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
#[cfg(all(feature = "alloc", not(feature = "std")))]
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
extern crate clear_on_drop;
|
|
||||||
extern crate curve25519_dalek;
|
extern crate curve25519_dalek;
|
||||||
#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))]
|
#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))]
|
||||||
extern crate merlin;
|
extern crate merlin;
|
||||||
|
|
@ -248,6 +247,7 @@ extern crate rand;
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
extern crate serde;
|
extern crate serde;
|
||||||
extern crate sha2;
|
extern crate sha2;
|
||||||
|
extern crate zeroize;
|
||||||
|
|
||||||
#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))]
|
#[cfg(all(any(feature = "batch", feature = "batch_deterministic"), any(feature = "std", feature = "alloc")))]
|
||||||
mod batch;
|
mod batch;
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,6 @@
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
|
|
||||||
use clear_on_drop::clear::Clear;
|
|
||||||
|
|
||||||
use curve25519_dalek::constants;
|
use curve25519_dalek::constants;
|
||||||
use curve25519_dalek::digest::generic_array::typenum::U64;
|
use curve25519_dalek::digest::generic_array::typenum::U64;
|
||||||
use curve25519_dalek::digest::Digest;
|
use curve25519_dalek::digest::Digest;
|
||||||
|
|
@ -32,13 +30,19 @@ use serde::{Deserialize, Serialize};
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserializer, Serializer};
|
use serde::{Deserializer, Serializer};
|
||||||
|
|
||||||
|
use zeroize::Zeroize;
|
||||||
|
|
||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::errors::*;
|
use crate::errors::*;
|
||||||
use crate::public::*;
|
use crate::public::*;
|
||||||
use crate::signature::*;
|
use crate::signature::*;
|
||||||
|
|
||||||
/// An EdDSA secret key.
|
/// An EdDSA secret key.
|
||||||
#[derive(Default)] // we derive Default in order to use the clear() method in Drop
|
///
|
||||||
|
/// Instances of this secret are automatically overwritten with zeroes when they
|
||||||
|
/// fall out of scope.
|
||||||
|
#[derive(Zeroize)]
|
||||||
|
#[zeroize(drop)] // Overwrite secret key material with null bytes when it goes out of scope.
|
||||||
pub struct SecretKey(pub(crate) [u8; SECRET_KEY_LENGTH]);
|
pub struct SecretKey(pub(crate) [u8; SECRET_KEY_LENGTH]);
|
||||||
|
|
||||||
impl Debug for SecretKey {
|
impl Debug for SecretKey {
|
||||||
|
|
@ -47,13 +51,6 @@ 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 AsRef<[u8]> for SecretKey {
|
impl AsRef<[u8]> for SecretKey {
|
||||||
fn as_ref(&self) -> &[u8] {
|
fn as_ref(&self) -> &[u8] {
|
||||||
self.as_bytes()
|
self.as_bytes()
|
||||||
|
|
@ -223,6 +220,9 @@ impl<'d> Deserialize<'d> for SecretKey {
|
||||||
/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation
|
/// upper half is used a sort of half-baked, ill-designed² pseudo-domain-separation
|
||||||
/// "nonce"-like thing, which is used during signature production by
|
/// "nonce"-like thing, which is used during signature production by
|
||||||
/// concatenating it with the message to be signed before the message is hashed.
|
/// concatenating it with the message to be signed before the message is hashed.
|
||||||
|
///
|
||||||
|
/// Instances of this secret are automatically overwritten with zeroes when they
|
||||||
|
/// fall out of scope.
|
||||||
//
|
//
|
||||||
// ¹ This results in a slight bias towards non-uniformity at one spectrum of
|
// ¹ This results in a slight bias towards non-uniformity at one spectrum of
|
||||||
// the range of valid keys. Oh well: not my idea; not my problem.
|
// the range of valid keys. Oh well: not my idea; not my problem.
|
||||||
|
|
@ -250,20 +250,13 @@ impl<'d> Deserialize<'d> for SecretKey {
|
||||||
// same signature scheme, and which both fail in exactly the same way. For a
|
// same signature scheme, and which both fail in exactly the same way. For a
|
||||||
// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on
|
// better-designed, Schnorr-based signature scheme, see Trevor Perrin's work on
|
||||||
// "generalised EdDSA" and "VXEdDSA".
|
// "generalised EdDSA" and "VXEdDSA".
|
||||||
#[derive(Default)] // we derive Default in order to use the clear() method in Drop
|
#[derive(Zeroize)]
|
||||||
|
#[zeroize(drop)] // Overwrite secret key material with null bytes when it goes out of scope.
|
||||||
pub struct ExpandedSecretKey {
|
pub struct ExpandedSecretKey {
|
||||||
pub(crate) key: Scalar,
|
pub(crate) key: Scalar,
|
||||||
pub(crate) nonce: [u8; 32],
|
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> From<&'a SecretKey> for ExpandedSecretKey {
|
impl<'a> From<&'a SecretKey> for ExpandedSecretKey {
|
||||||
/// Construct an `ExpandedSecretKey` from a `SecretKey`.
|
/// Construct an `ExpandedSecretKey` from a `SecretKey`.
|
||||||
///
|
///
|
||||||
|
|
@ -554,3 +547,23 @@ impl<'d> Deserialize<'d> for ExpandedSecretKey {
|
||||||
deserializer.deserialize_bytes(ExpandedSecretKeyVisitor)
|
deserializer.deserialize_bytes(ExpandedSecretKeyVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn secret_key_zeroize_on_drop() {
|
||||||
|
let secret_ptr: *const u8;
|
||||||
|
|
||||||
|
{ // scope for the secret to ensure it's been dropped
|
||||||
|
let secret = SecretKey::from_bytes(&[0x15u8; 32][..]).unwrap();
|
||||||
|
|
||||||
|
secret_ptr = secret.0.as_ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
let memory: &[u8] = unsafe { ::std::slice::from_raw_parts(secret_ptr, 32) };
|
||||||
|
|
||||||
|
assert!(!memory.contains(&0x15));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue