From 1d46e9571ff6ef13e6e25e2feb0eb55139ae190f Mon Sep 17 00:00:00 2001 From: Luke Parker Date: Sun, 12 Nov 2023 09:14:59 -0500 Subject: [PATCH] Implement Zeroize for SHA 1..=3 and Blake2 --- Cargo.lock | 52 +++++++++++++++++++++++++++++++++++++++ blake2/Cargo.toml | 4 ++- blake2/src/macros.rs | 18 ++++++++++++-- blake2/src/simd/simdty.rs | 5 ++++ sha1/Cargo.toml | 4 ++- sha1/src/lib.rs | 14 +++++++++++ sha2/Cargo.toml | 4 ++- sha2/src/core_api.rs | 41 ++++++++++++++++++++++++++++-- sha3/src/state.rs | 9 ++++++- 9 files changed, 143 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b676d7157..85973249d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,6 +8,7 @@ version = "0.10.6" dependencies = [ "digest", "hex-literal", + "zeroize", ] [[package]] @@ -188,6 +189,24 @@ version = "0.5.20+deprecated" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + [[package]] name = "ripemd" version = "0.1.3" @@ -205,6 +224,7 @@ dependencies = [ "digest", "hex-literal", "sha1-asm", + "zeroize", ] [[package]] @@ -225,6 +245,7 @@ dependencies = [ "digest", "hex-literal", "sha2-asm", + "zeroize", ] [[package]] @@ -276,6 +297,17 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tiger" version = "0.2.1" @@ -290,6 +322,12 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + [[package]] name = "version_check" version = "0.9.4" @@ -319,3 +357,17 @@ name = "zeroize" version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/blake2/Cargo.toml b/blake2/Cargo.toml index adb06e061..8332d500f 100644 --- a/blake2/Cargo.toml +++ b/blake2/Cargo.toml @@ -13,6 +13,7 @@ categories = ["cryptography", "no-std"] [dependencies] digest = { version = "0.10.7", features = ["mac"] } +zeroize = { version = "1", features = ["derive"], optional = true } [dev-dependencies] digest = { version = "0.10.7", features = ["dev"] } @@ -20,9 +21,10 @@ hex-literal = "0.2.2" [features] default = ["std"] -std = ["digest/std"] +std = ["digest/std", "zeroize?/std"] reset = [] # Enable reset functionality simd = [] simd_opt = ["simd"] simd_asm = ["simd_opt"] size_opt = [] # Optimize for code size. Removes some `inline(always)` +zeroize = ["dep:zeroize"] # Implement Zeroize for Digest implementors diff --git a/blake2/src/macros.rs b/blake2/src/macros.rs index 917a212c8..2049cbd6d 100644 --- a/blake2/src/macros.rs +++ b/blake2/src/macros.rs @@ -9,7 +9,7 @@ macro_rules! blake2_impl { pub struct $name { h: [$vec; 2], t: u64, - #[cfg(feature = "reset")] + #[cfg(any(feature = "reset", feature = "zeroize"))] h0: [$vec; 2], } @@ -86,7 +86,7 @@ macro_rules! blake2_impl { Self::iv1() ^ $vec::new(p[4], p[5], p[6], p[7]), ]; $name { - #[cfg(feature = "reset")] + #[cfg(any(feature = "reset", feature = "zeroize"))] h0: h.clone(), h, t: 0, @@ -243,6 +243,20 @@ macro_rules! blake2_impl { } } + #[cfg(feature = "zeroize")] + impl zeroize::Zeroize for $name { + fn zeroize(&mut self) { + self.h.zeroize(); + self.t.zeroize(); + + // Because the hasher is now in an invalid state, restore the starting state + // This makes Zeroize equivalent to reset *yet using a zero-write the compiler + // hopefully shouldn't be able to optimize out* + // The following lines may be optimized out if no further use occurs, which is fine + self.h = self.h0; + } + } + impl fmt::Debug for $name { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str(concat!(stringify!($name), " { ... }")) diff --git a/blake2/src/simd/simdty.rs b/blake2/src/simd/simdty.rs index 008b8b48c..5433c84e7 100644 --- a/blake2/src/simd/simdty.rs +++ b/blake2/src/simd/simdty.rs @@ -7,6 +7,9 @@ #![allow(dead_code, non_camel_case_types)] +#[cfg(feature = "zeroize")] +use zeroize::Zeroize; + use crate::as_bytes::Safe; #[cfg(feature = "simd")] @@ -14,6 +17,7 @@ macro_rules! decl_simd { ($($decl:item)*) => { $( #[derive(Clone, Copy, Debug)] + #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[repr(simd)] $decl )* @@ -25,6 +29,7 @@ macro_rules! decl_simd { ($($decl:item)*) => { $( #[derive(Clone, Copy, Debug)] + #[cfg_attr(feature = "zeroize", derive(Zeroize))] #[repr(C)] $decl )* diff --git a/sha1/Cargo.toml b/sha1/Cargo.toml index 3f4b8d3ff..672d87464 100644 --- a/sha1/Cargo.toml +++ b/sha1/Cargo.toml @@ -14,6 +14,7 @@ categories = ["cryptography", "no-std"] [dependencies] digest = "0.10.7" cfg-if = "1.0" +zeroize = { version = "1", optional = true } [target.'cfg(any(target_arch = "aarch64", target_arch = "x86", target_arch = "x86_64"))'.dependencies] cpufeatures = "0.2" @@ -25,7 +26,7 @@ hex-literal = "0.2.2" [features] default = ["std"] -std = ["digest/std"] +std = ["digest/std", "zeroize?/std"] oid = ["digest/oid"] # Enable OID support. WARNING: Bumps MSRV to 1.57 asm = ["sha1-asm"] # WARNING: this feature SHOULD NOT be enabled by library crates # Use assembly backend for LoongArch64 targets @@ -33,6 +34,7 @@ asm = ["sha1-asm"] # WARNING: this feature SHOULD NOT be enabled by library crat loongarch64_asm = [] compress = [] # Expose compress function force-soft = [] # Force software implementation +zeroize = ["dep:zeroize"] # Implement Zeroize for Digest implementors [package.metadata.docs.rs] all-features = true diff --git a/sha1/src/lib.rs b/sha1/src/lib.rs index 25e867e0f..e5f935388 100644 --- a/sha1/src/lib.rs +++ b/sha1/src/lib.rs @@ -150,6 +150,20 @@ impl AlgorithmName for Sha1Core { } } +#[cfg(feature = "zeroize")] +impl zeroize::Zeroize for Sha1Core { + fn zeroize(&mut self) { + self.h.zeroize(); + self.block_len.zeroize(); + + // Because the hasher is now in an invalid state, restore the starting state + // This makes Zeroize equivalent to reset *yet using a zero-write the compiler hopefully + // shouldn't be able to optimize out* + // The following lines may be optimized out if no further use occurs, which is fine + self.h = Self::default().h; + } +} + impl fmt::Debug for Sha1Core { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("Sha1Core { ... }") diff --git a/sha2/Cargo.toml b/sha2/Cargo.toml index a3dafeaa2..6ab7f5e23 100644 --- a/sha2/Cargo.toml +++ b/sha2/Cargo.toml @@ -17,6 +17,7 @@ categories = ["cryptography", "no-std"] [dependencies] digest = "0.10.7" cfg-if = "1.0" +zeroize = { version = "1", optional = true } [target.'cfg(any(target_arch = "aarch64", target_arch = "x86_64", target_arch = "x86"))'.dependencies] cpufeatures = "0.2" @@ -28,7 +29,7 @@ hex-literal = "0.2.2" [features] default = ["std"] -std = ["digest/std"] +std = ["digest/std", "zeroize?/std"] oid = ["digest/oid"] # Enable OID support. WARNING: Bumps MSRV to 1.57 asm = ["sha2-asm"] # WARNING: this feature SHOULD NOT be enabled by library crates # Use assembly backend for LoongArch64 targets @@ -37,6 +38,7 @@ loongarch64_asm = [] compress = [] # Expose compress functions force-soft = [] # Force software implementation asm-aarch64 = ["asm"] # DEPRECATED: use `asm` instead +zeroize = ["dep:zeroize"] # Implement Zeroize for Digest implementors [package.metadata.docs.rs] all-features = true diff --git a/sha2/src/core_api.rs b/sha2/src/core_api.rs index cfec02a64..e15f3a869 100644 --- a/sha2/src/core_api.rs +++ b/sha2/src/core_api.rs @@ -16,6 +16,8 @@ use digest::{ /// i.e. 224 and 256 bits respectively. #[derive(Clone)] pub struct Sha256VarCore { + #[cfg(feature = "zeroize")] + output_size: usize, state: consts::State256, block_len: u64, } @@ -53,7 +55,12 @@ impl VariableOutputCore for Sha256VarCore { _ => return Err(InvalidOutputSize), }; let block_len = 0; - Ok(Self { state, block_len }) + Ok(Self { + #[cfg(feature = "zeroize")] + output_size, + state, + block_len, + }) } #[inline] @@ -75,6 +82,20 @@ impl AlgorithmName for Sha256VarCore { } } +#[cfg(feature = "zeroize")] +impl zeroize::Zeroize for Sha256VarCore { + fn zeroize(&mut self) { + self.state.zeroize(); + self.block_len.zeroize(); + + // Because the hasher is now in an invalid state, restore the starting state + // This makes Zeroize equivalent to reset *yet using a zero-write the compiler hopefully + // shouldn't be able to optimize out* + // The following lines may be optimized out if no further use occurs, which is fine + self.state = Self::new(self.output_size).unwrap().state; + } +} + impl fmt::Debug for Sha256VarCore { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -88,6 +109,8 @@ impl fmt::Debug for Sha256VarCore { /// i.e. 224, 256, 384, and 512 bits respectively. #[derive(Clone)] pub struct Sha512VarCore { + #[cfg(feature = "zeroize")] + output_size: usize, state: consts::State512, block_len: u128, } @@ -127,7 +150,12 @@ impl VariableOutputCore for Sha512VarCore { _ => return Err(InvalidOutputSize), }; let block_len = 0; - Ok(Self { state, block_len }) + Ok(Self { + #[cfg(feature = "zeroize")] + output_size, + state, + block_len, + }) } #[inline] @@ -149,6 +177,15 @@ impl AlgorithmName for Sha512VarCore { } } +#[cfg(feature = "zeroize")] +impl zeroize::Zeroize for Sha512VarCore { + fn zeroize(&mut self) { + self.state.zeroize(); + self.block_len.zeroize(); + self.state = Self::new(self.output_size).unwrap().state; + } +} + impl fmt::Debug for Sha512VarCore { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/sha3/src/state.rs b/sha3/src/state.rs index ce3785f75..2309ceaba 100644 --- a/sha3/src/state.rs +++ b/sha3/src/state.rs @@ -20,10 +20,17 @@ impl Default for Sha3State { } } +#[cfg(feature = "zeroize")] +impl Zeroize for Sha3State { + fn zeroize(&mut self) { + self.state.zeroize(); + } +} + #[cfg(feature = "zeroize")] impl Drop for Sha3State { fn drop(&mut self) { - self.state.zeroize(); + self.zeroize(); } }