Skip to content

Commit

Permalink
Manually implement Zeroize
Browse files Browse the repository at this point in the history
Removes dependence on proc-macros when enabling the zeroize feature.
  • Loading branch information
gardk committed Jul 11, 2024
1 parent 8416b16 commit ffa18f2
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 8 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ memmap2 = { version = "0.9", optional = true }
rayon-core = { version = "1.12.1", optional = true }
serde = { version = "1.0", default-features = false, features = ["derive"], optional = true }
serde_bytes = { version = "0.11.15", optional = true }
zeroize = { version = "1", default-features = false, features = ["zeroize_derive"], optional = true }
zeroize = { version = "1", default-features = false, optional = true }

[dev-dependencies]
hmac = "0.12.0"
Expand Down
87 changes: 80 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ use arrayvec::{ArrayString, ArrayVec};
use core::cmp;
use core::fmt;
use platform::{Platform, MAX_SIMD_DEGREE, MAX_SIMD_DEGREE_OR_2};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;

/// The number of bytes in a [`Hash`](struct.Hash.html), 32.
pub const OUT_LEN: usize = 32;
Expand Down Expand Up @@ -216,7 +218,6 @@ fn counter_high(counter: u64) -> u32 {
/// [`from_hex`]: #method.from_hex
/// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
/// [`FromStr`]: https://doc.rust-lang.org/std/str/trait.FromStr.html
#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[derive(Clone, Copy, Hash)]
pub struct Hash(
Expand Down Expand Up @@ -310,6 +311,14 @@ impl core::str::FromStr for Hash {
}
}

#[cfg(feature = "zeroize")]
impl Zeroize for Hash {
fn zeroize(&mut self) {
let Self(bytes) = self;
bytes.as_mut_slice().zeroize();
}
}

// A proper implementation of constant time equality is tricky, and we get it from the
// constant_time_eq crate instead of rolling our own. However, that crate isn't compatible with
// Miri, so we roll our own just for that.
Expand Down Expand Up @@ -421,15 +430,13 @@ impl std::error::Error for HexError {}
// Each chunk or parent node can produce either a 32-byte chaining value or, by
// setting the ROOT flag, any number of final output bytes. The Output struct
// captures the state just prior to choosing between those two possibilities.
#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))]
#[derive(Clone)]
struct Output {
input_chaining_value: CVWords,
block: [u8; 64],
block_len: u8,
counter: u64,
flags: u8,
#[cfg_attr(feature = "zeroize", zeroize(skip))]
platform: Platform,
}

Expand Down Expand Up @@ -465,16 +472,34 @@ impl Output {
}
}

#[cfg(feature = "zeroize")]
impl Zeroize for Output {
fn zeroize(&mut self) {
let Self {
input_chaining_value,
block,
block_len,
counter,
flags,
platform: _,
} = self;

input_chaining_value.as_mut_slice().zeroize();
block.as_mut_slice().zeroize();
block_len.zeroize();
counter.zeroize();
flags.zeroize();
}
}

#[derive(Clone)]
#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))]
struct ChunkState {
cv: CVWords,
chunk_counter: u64,
buf: [u8; BLOCK_LEN],
buf_len: u8,
blocks_compressed: u8,
flags: u8,
#[cfg_attr(feature = "zeroize", zeroize(skip))]
platform: Platform,
}

Expand Down Expand Up @@ -577,6 +602,28 @@ impl fmt::Debug for ChunkState {
}
}

#[cfg(feature = "zeroize")]
impl Zeroize for ChunkState {
fn zeroize(&mut self) {
let Self {
cv,
chunk_counter,
buf,
buf_len,
blocks_compressed,
flags,
platform: _,
} = self;

cv.as_mut_slice().zeroize();
chunk_counter.zeroize();
buf.as_mut_slice().zeroize();
buf_len.zeroize();
blocks_compressed.zeroize();
flags.zeroize();
}
}

// IMPLEMENTATION NOTE
// ===================
// The recursive function compress_subtree_wide(), implemented below, is the
Expand Down Expand Up @@ -990,7 +1037,6 @@ fn parent_node_output(
/// # }
/// ```
#[derive(Clone)]
#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))]
pub struct Hasher {
key: CVWords,
chunk_state: ChunkState,
Expand Down Expand Up @@ -1537,6 +1583,21 @@ impl std::io::Write for Hasher {
}
}

#[cfg(feature = "zeroize")]
impl Zeroize for Hasher {
fn zeroize(&mut self) {
let Self {
key,
chunk_state,
cv_stack,
} = self;

key.as_mut_slice().zeroize();
chunk_state.zeroize();
cv_stack.zeroize();
}
}

/// An incremental reader for extended output, returned by
/// [`Hasher::finalize_xof`](struct.Hasher.html#method.finalize_xof).
///
Expand All @@ -1560,7 +1621,6 @@ impl std::io::Write for Hasher {
/// from an unknown position in the output stream to recover its block index. Callers with strong
/// secret keys aren't affected in practice, but secret offsets are a [design
/// smell](https://en.wikipedia.org/wiki/Design_smell) in any case.
#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize))]
#[derive(Clone)]
pub struct OutputReader {
inner: Output,
Expand Down Expand Up @@ -1672,3 +1732,16 @@ impl std::io::Seek for OutputReader {
Ok(self.position())
}
}

#[cfg(feature = "zeroize")]
impl Zeroize for OutputReader {
fn zeroize(&mut self) {
let Self {
inner,
position_within_block,
} = self;

inner.zeroize();
position_within_block.zeroize();
}
}

0 comments on commit ffa18f2

Please sign in to comment.