diff --git a/Cargo.lock b/Cargo.lock index 9064eacd..137c91a6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1658,6 +1658,7 @@ dependencies = [ "ff-group-tests", "group 0.13.0", "rand_core 0.6.4", + "rustversion", "sha2 0.9.9", "subtle", "zeroize", @@ -1995,6 +1996,7 @@ dependencies = [ "k256", "multiexp", "rand_core 0.6.4", + "rustversion", "thiserror", "zeroize", ] @@ -2798,7 +2800,10 @@ dependencies = [ "blake2", "digest 0.10.6", "merlin 3.0.0", + "rustversion", "sha2 0.10.6", + "subtle", + "zeroize", ] [[package]] @@ -5046,6 +5051,7 @@ dependencies = [ "hex", "lazy_static", "rand_core 0.6.4", + "rustversion", "subtle", "zeroize", ] @@ -5278,6 +5284,7 @@ dependencies = [ "group 0.13.0", "k256", "rand_core 0.6.4", + "rustversion", "zeroize", ] diff --git a/crypto/dalek-ff-group/Cargo.toml b/crypto/dalek-ff-group/Cargo.toml index 243c7405..7f2517be 100644 --- a/crypto/dalek-ff-group/Cargo.toml +++ b/crypto/dalek-ff-group/Cargo.toml @@ -13,12 +13,15 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] -rand_core = "0.6" -digest = "0.10" +rustversion = "1" zeroize = { version = "^1.5", features = ["zeroize_derive"] } subtle = "^2.4" +rand_core = "0.6" + +digest = "0.10" + ff = "0.13" group = "0.13" @@ -29,6 +32,3 @@ curve25519-dalek = "^3.2" [dev-dependencies] ff-group-tests = { path = "../ff-group-tests" } - -[features] -black_box = [] diff --git a/crypto/dalek-ff-group/src/lib.rs b/crypto/dalek-ff-group/src/lib.rs index 2c1faf8d..3d67ebca 100644 --- a/crypto/dalek-ff-group/src/lib.rs +++ b/crypto/dalek-ff-group/src/lib.rs @@ -38,14 +38,11 @@ use group::{ mod field; pub use field::FieldElement; -// Feature gated due to MSRV requirements -#[cfg(feature = "black_box")] -pub(crate) fn black_box(val: T) -> T { - core::hint::black_box(val) -} - -#[cfg(not(feature = "black_box"))] -pub(crate) fn black_box(val: T) -> T { +// Use black_box when possible +#[rustversion::since(1.66)] +use core::hint::black_box; +#[rustversion::before(1.66)] +fn black_box(val: T) -> T { val } diff --git a/crypto/dleq/Cargo.toml b/crypto/dleq/Cargo.toml index 1a34f525..2a83feb1 100644 --- a/crypto/dleq/Cargo.toml +++ b/crypto/dleq/Cargo.toml @@ -12,6 +12,8 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +rustversion = "1" + thiserror = { version = "1", optional = true } rand_core = "0.6" @@ -41,7 +43,6 @@ std = [] serialize = ["std"] # Needed for cross-group DLEqs -black_box = [] secure_capacity_difference = [] experimental = ["std", "thiserror", "multiexp"] diff --git a/crypto/dleq/src/cross_group/mod.rs b/crypto/dleq/src/cross_group/mod.rs index 4ee867d7..f97ee390 100644 --- a/crypto/dleq/src/cross_group/mod.rs +++ b/crypto/dleq/src/cross_group/mod.rs @@ -30,14 +30,11 @@ pub(crate) mod aos; mod bits; use bits::{BitSignature, Bits}; -// Feature gated due to MSRV requirements -#[cfg(feature = "black_box")] -pub(crate) fn black_box(val: T) -> T { - core::hint::black_box(val) -} - -#[cfg(not(feature = "black_box"))] -pub(crate) fn black_box(val: T) -> T { +// Use black_box when possible +#[rustversion::since(1.66)] +use core::hint::black_box; +#[rustversion::before(1.66)] +fn black_box(val: T) -> T { val } diff --git a/crypto/ed448/Cargo.toml b/crypto/ed448/Cargo.toml index f0715cdf..72dfbf94 100644 --- a/crypto/ed448/Cargo.toml +++ b/crypto/ed448/Cargo.toml @@ -13,6 +13,8 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +rustversion = "1" + lazy_static = "1" rand_core = "0.6" @@ -30,6 +32,3 @@ crypto-bigint = { version = "0.5", features = ["zeroize"] } hex = "0.4" ff-group-tests = { path = "../ff-group-tests" } - -[features] -black_box = [] diff --git a/crypto/ed448/src/backend.rs b/crypto/ed448/src/backend.rs index a4811cb0..002c338f 100644 --- a/crypto/ed448/src/backend.rs +++ b/crypto/ed448/src/backend.rs @@ -1,13 +1,10 @@ use zeroize::Zeroize; -// Feature gated due to MSRV requirements -#[cfg(feature = "black_box")] -pub(crate) fn black_box(val: T) -> T { - core::hint::black_box(val) -} - -#[cfg(not(feature = "black_box"))] -pub(crate) fn black_box(val: T) -> T { +// Use black_box when possible +#[rustversion::since(1.66)] +use core::hint::black_box; +#[rustversion::before(1.66)] +fn black_box(val: T) -> T { val } diff --git a/crypto/multiexp/Cargo.toml b/crypto/multiexp/Cargo.toml index 6e17de6a..d62a8062 100644 --- a/crypto/multiexp/Cargo.toml +++ b/crypto/multiexp/Cargo.toml @@ -13,6 +13,8 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +rustversion = "1" + zeroize = { version = "^1.5", features = ["zeroize_derive"] } ff = "0.13" @@ -27,5 +29,4 @@ k256 = { version = "0.13", features = ["bits"] } dalek-ff-group = { path = "../dalek-ff-group" } [features] -black_box = [] batch = ["rand_core"] diff --git a/crypto/multiexp/src/lib.rs b/crypto/multiexp/src/lib.rs index 0fc0a8c3..d5236e54 100644 --- a/crypto/multiexp/src/lib.rs +++ b/crypto/multiexp/src/lib.rs @@ -22,14 +22,11 @@ pub use batch::BatchVerifier; #[cfg(test)] mod tests; -// Feature gated due to MSRV requirements -#[cfg(feature = "black_box")] -pub(crate) fn black_box(val: T) -> T { - core::hint::black_box(val) -} - -#[cfg(not(feature = "black_box"))] -pub(crate) fn black_box(val: T) -> T { +// Use black_box when possible +#[rustversion::since(1.66)] +use core::hint::black_box; +#[rustversion::before(1.66)] +fn black_box(val: T) -> T { val } diff --git a/crypto/schnorr/src/aggregate.rs b/crypto/schnorr/src/aggregate.rs index 007cb4d8..2ec9170f 100644 --- a/crypto/schnorr/src/aggregate.rs +++ b/crypto/schnorr/src/aggregate.rs @@ -139,18 +139,12 @@ impl SchnorrAggregate { /// A signature aggregator capable of consuming signatures in order to produce an aggregate. #[allow(non_snake_case)] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Zeroize)] pub struct SchnorrAggregator { digest: DigestTranscript, sigs: Vec>, } -impl Zeroize for SchnorrAggregator { - fn zeroize(&mut self) { - self.sigs.zeroize(); - } -} - impl SchnorrAggregator { /// Create a new aggregator. /// diff --git a/crypto/transcript/Cargo.toml b/crypto/transcript/Cargo.toml index 6ef0a696..601322db 100644 --- a/crypto/transcript/Cargo.toml +++ b/crypto/transcript/Cargo.toml @@ -13,6 +13,11 @@ all-features = true rustdoc-args = ["--cfg", "docsrs"] [dependencies] +rustversion = "1" + +subtle = "^2.4" +zeroize = "^1.5" + digest = "0.10" blake2 = { version = "0.10", optional = true } diff --git a/crypto/transcript/src/lib.rs b/crypto/transcript/src/lib.rs index 272b2c45..74ddf928 100644 --- a/crypto/transcript/src/lib.rs +++ b/crypto/transcript/src/lib.rs @@ -2,6 +2,15 @@ #![no_std] ///! A transcript trait valid over a variety of transcript formats. +use zeroize::Zeroize; + +use digest::{ + typenum::{ + consts::U32, marker_traits::NonZero, type_operators::IsGreaterOrEqual, operator_aliases::GrEq, + }, + core_api::BlockSizeUser, + Digest, Output, HashMarker, +}; #[cfg(feature = "merlin")] mod merlin; @@ -12,13 +21,6 @@ pub use crate::merlin::MerlinTranscript; #[cfg(any(test, feature = "tests"))] pub mod tests; -use digest::{ - typenum::{ - consts::U32, marker_traits::NonZero, type_operators::IsGreaterOrEqual, operator_aliases::GrEq, - }, - Digest, Output, HashMarker, -}; - /// A transcript trait valid over a variety of transcript formats. pub trait Transcript: Send + Clone { type Challenge: Send + Sync + Clone + AsRef<[u8]>; @@ -134,6 +136,61 @@ impl Transcript for DigestTranscript { } } +// Digest doesn't implement Zeroize +// Implement Zeroize for DigestTranscript by writing twice the block size to the digest in an +// attempt to overwrite the internal hash state/any leftover bytes +impl Zeroize for DigestTranscript +where + D: BlockSizeUser, +{ + fn zeroize(&mut self) { + // Update in 4-byte chunks to reduce call quantity and enable word-level update optimizations + const WORD_SIZE: usize = 4; + + // block_size returns the block_size in bytes + // Use a ceil div in case the block size isn't evenly divisible by our word size + let words = (D::block_size() + (WORD_SIZE - 1)) / WORD_SIZE; + for _ in 0 .. (2 * words) { + self.0.update([255; WORD_SIZE]); + } + + // Hopefully, the hash state is now overwritten to the point no data is recoverable + // These writes may be optimized out if they're never read + // Attempt to get them marked as read + + #[rustversion::since(1.66)] + fn mark_read(transcript: &mut DigestTranscript) { + // Just get a challenge from the state + let mut challenge = core::hint::black_box(transcript.0.clone().finalize()); + challenge.as_mut().zeroize(); + } + + #[rustversion::before(1.66)] + fn mark_read(transcript: &mut DigestTranscript) { + // Get a challenge + let challenge = transcript.0.clone().finalize(); + + // Attempt to use subtle's, non-exposed black_box function, by creating a Choice from this + // challenge + + let mut read = 0; + for byte in challenge.as_ref() { + read ^= byte; + } + challenge.as_mut().zeroize(); + + // Since this Choice isn't further read, its creation may be optimized out, including its + // internal black_box + // This remains our best attempt + let mut choice = bool::from(subtle::Choice::from(read >> 7)); + read.zeroize(); + choice.zeroize(); + } + + mark_read(self) + } +} + /// The recommended transcript, guaranteed to be secure against length-extension attacks. #[cfg(feature = "recommended")] pub type RecommendedTranscript = DigestTranscript;