Repository: RustCrypto/hashes (language: rust)

Issue:
Zero memory on drop
I have a question about the hash memory management but also for other related repositories. As it seems `RustCrypto` use `GenericArray` and/or raw arrays for sensitive data like keys and internal buffers but the memory is not overwritten/zeroed on reset or when the buffer/struct is dropped for most crates, right? Are there any plans to change this?


Discussion / hints:
Yes, you are right, currently most crates do not zeroize inner state on drop. IIUC to implement it without hacks we will need either `generic-array` support (e.g. by implementing [`Zeroize`](https://docs.rs/zeroize/0.9.2/zeroize/trait.Zeroize.html) trait.) or const generics.
@newpavlov you can do a `Zeroize` impl without explicit support for `generic-array` by borrowing the contents of the latter as a byte slice and calling `zeroize()` on that. `IterMut` will also work.
@tarcieri Maybe we can transform the whole hash state into byte slice (using `unsafe` code) and call zeroize on it?
@newpavlov or you can just call zeroize on each value in its state in a `Drop` handler. `zeroize` is designed to support structured data. If they're `GenericArray` you just need to call `as_ref()` (or `as_slice()`?) first iirc

When const generics land it should be possible to derive a `Zeroize` impl and drop handler using `zeroize_derive` (the same thing would be possible with first-class `generic-array` support for zeroize, but that doesn't exist).
@fxha is there a particular hash function or set of them you'd specifically like to have this in?

We can add optional `zeroize` features to high-priority crates.
I think it's good practice to (optionally) erase secret data and don't leak keys in memory.
I agree (in fact I [wrote the most popular crate for it](https://crates.io/crates/zeroize)).

However hash functions are a bit of an odd fish in this regard. Ones based on the Merkle–Damgård construction (which constitute a large number of the ones in this repo, including the popular `sha2`) effectively output their internal state upon finalization (which is what enables length extension attacks). If that internal state were reversible to a secret, that would be a preimage attack against the underlying hash function.

I think the hash functions where this would be relevant are, as you pointed out, ones with keys, i.e. ones which provide PRFs.

As far as I know the only one in this particular repo that applies to is `blake2`.
Zeroize cannot prevent the type from being used after zeroing.  Internal states often include constants, so zeroizing can leave an internal state that functions but provides less security.
@tarcieri I don't quite remember, but I was using Blake2 and noticed that my key was copied or something like this. Cleaning the internal state isn't a high priority but basic cleanup should be provided.
I wonder if we should make the reset capability optional... It would allow us to remove the [caching](https://github.com/RustCrypto/hashes/blob/master/blake2/src/blake2.rs#L28-L30) of initial state and thus secret key inside hasher state will  be overwritten as soon as first block of data has been processed. How often in your opinion users need to reset MAC states?
How about adding the Zeroize trait for blake2 at least? I am trying to implement a noise-like protocol where hmac-blake2 is used as a KDF. Right now I am using libsodium including sodium_malloc/free to make sure ephemeral secrecy is guaranteed.

The lack of well thought-through zeroization in RustCrypto is a major blocker for moving away from sodium.
I think an optional `zeroize` feature which adds a `Drop` impl that zeroizes the relevant state is probably the best approach, along with a `ZeroizeOnDrop` impl.

The problem with `Zeroize` is it might leave the hasher in an invalid state without consuming it.
https://github.com/RustCrypto/hashes/pull/449
Hi! bumping this again because a lot of project effectively need `digest::Digest` to implement `zeroize::Zeroize`. Do you need help with this? 
1) Impl `Zeroize` for Digest such that after `zeroize()`, it writes the valid state. If the `Zeroize` impl is ordered with respect to the post-write, then this will leave the hasher in a valid state. While this post-write may be optimized out if unused, if reused, it should be valid. No?

(I'm not sure this is valid due to the ordering pre-condition. Doesn't Zeroize use a volatile only guaranteed to be ordered with regard to other volatiles? If so, I think that'd make all use of Zeroize unsafe, so I doubt it, but I obviously may be missing something...)

2) I would love `Zeroize` on all items satisfying `Digest` because *the resulting state is a secret*. It's used as a nonce in a variety of systems. This leaves me to flood `Digest` with `block size` of data (if a block based hash), attempting to overwrite the prior state, and then get the resulting Digest marked used (to prevent the block of data written from being optimized out).
> Doesn't Zeroize use a volatile only guaranteed to be ordered with regard to other volatiles?

You are thinking of some outdated guidelines. Please read this section of the `zeroize` documentation:

https://docs.rs/zeroize/latest/zeroize/#what-guarantees-does-this-crate-provide
In that case, the sole question is if a further write is guaranteed to be ordered, and if not, how to trigger a read it is ordered in regard to (not my field of expertise). Regardless, sounds like my proposed solution could/should work?
I would probably suggest just adding a `Drop` impl, rather than a `Zeroize` impl (you could also add a `ZeroizeOnDrop` impl).

The problem with adding a `Zeroize` impl is there's a potential "use-after-zeroize" condition that can occur, which can't occur with a `Drop` impl.

Edit: [oops, I already said this](https://github.com/RustCrypto/hashes/issues/87#issuecomment-1382929059)
Right, I read the history, yet if after the zeroize we re-write the starting state... it's use-after-init-after-zeroize. That shouldn't introduce any unsafety.
It isn't a "safety" issue in the Rust sense, but the problem is `zeroize` might be called on it, then it could be reused in a context where the zeroization was unexpected.

Though I suppose there's an argument to be made that it's no different from `Reset::reset`, and adding a `Zeroize` impl would not penalize users who don't care about zeroization.
If a user explicitly calls Zeroize, which is just a stronger reset in this context, and then it's reused 'unexpectedly', I'd argue that's the users fault, yes.
>it's use-after-init-after-zeroize. That shouldn't introduce any unsafety.

It's a logical bug. Zeroized state may not be valid for a given hash function. In the worst case scenario, it may even break safety assumptions used by implementation.

Fix patch:
diff --git a/.github/workflows/tiger.yml b/.github/workflows/tiger.yml
index 43a7334fa..29e70fd98 100644
--- a/.github/workflows/tiger.yml
+++ b/.github/workflows/tiger.yml
@@ -24,7 +24,7 @@ jobs:
   set-msrv:
     uses: RustCrypto/actions/.github/workflows/set-msrv.yml@master
     with:
-        msrv: 1.71.0
+        msrv: 1.74.0
 
   build:
     needs: set-msrv
diff --git a/Cargo.lock b/Cargo.lock
index 9f84a908f..8a841bf2e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -96,8 +96,7 @@ dependencies = [
 [[package]]
 name = "digest"
 version = "0.11.0-pre.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f5c2be52eb4dc1e4456d89eaa0df751889c31dd95a69efc57e92e6e3593a1fc"
+source = "git+https://github.com/RustCrypto/traits#b4ec63b0e6b94fad7c311b6b77ae0b59d3c3b18e"
 dependencies = [
  "blobby",
  "block-buffer",
@@ -272,7 +271,6 @@ dependencies = [
  "digest",
  "hex-literal",
  "keccak",
- "zeroize",
 ]
 
 [[package]]
@@ -325,6 +323,9 @@ name = "threefish"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "a693d0c8cf16973fac5a93fbe47b8c6452e7097d4fcac49f3d7a18e39c76e62e"
+dependencies = [
+ "zeroize",
+]
 
 [[package]]
 name = "tiger"
diff --git a/Cargo.toml b/Cargo.toml
index 26637761e..322ec35f4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -26,3 +26,6 @@ members = [
 
 [profile.dev]
 opt-level = 2
+
+[patch.crates-io]
+digest = { git = 'https://github.com/RustCrypto/traits' }
diff --git a/README.md b/README.md
index 48ac4541d..f104f7100 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ Additionally all crates do not require the standard library (i.e. `no_std` capab
 | [Skein] | [`skein`] | [![crates.io](https://img.shields.io/crates/v/skein.svg)](https://crates.io/crates/skein) | [![Documentation](https://docs.rs/skein/badge.svg)](https://docs.rs/skein) | ![MSRV 1.71][msrv-1.71] | :green_heart: |
 | [SM3] (OSCCA GM/T 0004-2012) | [`sm3`] | [![crates.io](https://img.shields.io/crates/v/sm3.svg)](https://crates.io/crates/sm3) | [![Documentation](https://docs.rs/sm3/badge.svg)](https://docs.rs/sm3) | ![MSRV 1.71][msrv-1.71] | :green_heart: |
 | [Streebog] (GOST R 34.11-2012) | [`streebog`] | [![crates.io](https://img.shields.io/crates/v/streebog.svg)](https://crates.io/crates/streebog) | [![Documentation](https://docs.rs/streebog/badge.svg)](https://docs.rs/streebog) | ![MSRV 1.71][msrv-1.71] | :yellow_heart: |
-| [Tiger] | [`tiger`] | [![crates.io](https://img.shields.io/crates/v/tiger.svg)](https://crates.io/crates/tiger) | [![Documentation](https://docs.rs/tiger/badge.svg)](https://docs.rs/tiger) | ![MSRV 1.71][msrv-1.71] | :green_heart: |
+| [Tiger] | [`tiger`] | [![crates.io](https://img.shields.io/crates/v/tiger.svg)](https://crates.io/crates/tiger) | [![Documentation](https://docs.rs/tiger/badge.svg)](https://docs.rs/tiger) | ![MSRV 1.74][msrv-1.74] | :green_heart: |
 | [Whirlpool] | [`whirlpool`] | [![crates.io](https://img.shields.io/crates/v/whirlpool.svg)](https://crates.io/crates/whirlpool) | [![Documentation](https://docs.rs/whirlpool/badge.svg)](https://docs.rs/whirlpool) | ![MSRV 1.71][msrv-1.71] | :green_heart: |
 
 NOTE: the [`blake3`] crate implements the `digest` traits used by the rest of the hashes in this repository, but is maintained by the BLAKE3 team.
@@ -235,6 +235,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
 [deps-link]: https://deps.rs/repo/github/RustCrypto/hashes
 [msrv-1.71]: https://img.shields.io/badge/rustc-1.71.0+-blue.svg
 [msrv-1.72]: https://img.shields.io/badge/rustc-1.72.0+-blue.svg
+[msrv-1.74]: https://img.shields.io/badge/rustc-1.74.0+-blue.svg
 
 [//]: # (crates)
 
diff --git a/ascon-hash/src/lib.rs b/ascon-hash/src/lib.rs
index 2696f0580..28e88ea4d 100644
--- a/ascon-hash/src/lib.rs
+++ b/ascon-hash/src/lib.rs
@@ -1,10 +1,10 @@
 #![no_std]
-#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![doc = include_str!("../README.md")]
 #![doc(
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs)]
 
 use core::marker::PhantomData;
diff --git a/belt-hash/Cargo.toml b/belt-hash/Cargo.toml
index c3e4d8695..dff4e0db5 100644
--- a/belt-hash/Cargo.toml
+++ b/belt-hash/Cargo.toml
@@ -24,3 +24,8 @@ hex-literal = "0.4"
 default = ["oid", "std"]
 std = ["digest/std"]
 oid = ["digest/oid"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/belt-hash/src/lib.rs b/belt-hash/src/lib.rs
index a2550bb3f..7207eb11c 100644
--- a/belt-hash/src/lib.rs
+++ b/belt-hash/src/lib.rs
@@ -1,10 +1,10 @@
 #![no_std]
 #![doc = include_str!("../README.md")]
-#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![doc(
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs, rust_2018_idioms)]
 #![forbid(unsafe_code)]
 
@@ -24,6 +24,9 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 const U32_MASK: u128 = (1 << 32) - 1;
 const H0: [u32; 8] = [
     0xC8BA94B1, 0x3BF5080A, 0x8E006D36, 0xE45D4A58, 0x9DFA0485, 0xACC7B61B, 0xC2722E25, 0x0DCEFD02,
@@ -37,6 +40,9 @@ pub struct BeltHashCore {
     h: [u32; 8],
 }
 
+/// BelT hasher state.
+pub type BeltHash = CoreWrapper<BeltHashCore>;
+
 impl BeltHashCore {
     fn compress_block(&mut self, block: &Block<Self>) {
         let x1 = [
@@ -134,8 +140,19 @@ impl AssociatedOid for BeltHashCore {
     const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.112.0.2.0.34.101.31.81");
 }
 
-/// BelT hasher state.
-pub type BeltHash = CoreWrapper<BeltHashCore>;
+impl Drop for BeltHashCore {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.r.zeroize();
+            self.s.zeroize();
+            self.h.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for BeltHashCore {}
 
 /// Compression function described in the section 6.3.2
 #[inline(always)]
diff --git a/blake2/Cargo.toml b/blake2/Cargo.toml
index e2912552c..95d265611 100644
--- a/blake2/Cargo.toml
+++ b/blake2/Cargo.toml
@@ -22,8 +22,13 @@ hex-literal = "0.4"
 [features]
 default = ["std"]
 std = ["digest/std"]
+zeroize = ["digest/zeroize"]
 reset = [] # Enable reset functionality
 #simd = []
 #simd_opt = ["simd"]
 #simd_asm = ["simd_opt"]
 size_opt = [] # Optimize for code size. Removes some `inline(always)`
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/blake2/src/lib.rs b/blake2/src/lib.rs
index d884d1f9f..266c39ec5 100644
--- a/blake2/src/lib.rs
+++ b/blake2/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs, rust_2018_idioms)]
 #![cfg_attr(feature = "simd", feature(platform_intrinsics, repr_simd))]
 #![cfg_attr(feature = "simd", allow(incomplete_features))]
@@ -30,6 +31,9 @@ use digest::{
 #[cfg(feature = "reset")]
 use digest::{FixedOutputReset, Reset};
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 mod as_bytes;
 mod consts;
 
diff --git a/blake2/src/macros.rs b/blake2/src/macros.rs
index bf92d5039..3c9459ff6 100644
--- a/blake2/src/macros.rs
+++ b/blake2/src/macros.rs
@@ -246,6 +246,18 @@ macro_rules! blake2_impl {
                 f.write_str(concat!(stringify!($name), " { ... }"))
             }
         }
+
+        impl Drop for $name {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.h.zeroize();
+                    self.t.zeroize();
+                }
+            }
+        }
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $name {}
     };
 }
 
@@ -426,5 +438,28 @@ macro_rules! blake2_mac_impl {
                 write!(f, "{}{} {{ ... }}", stringify!($name), OutSize::USIZE)
             }
         }
+
+        impl<OutSize> Drop for $name<OutSize>
+        where
+            OutSize: ArraySize + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    // `self.core` zeroized by its `Drop` impl
+                    self.buffer.zeroize();
+                    #[cfg(feature = "reset")]
+                    self.key_block.zeroize();
+                }
+            }
+        }
+        #[cfg(feature = "zeroize")]
+        impl<OutSize> ZeroizeOnDrop for $name<OutSize>
+        where
+            OutSize: ArraySize + IsLessOrEqual<$max_size>,
+            LeEq<OutSize, $max_size>: NonZero,
+        {
+        }
     };
 }
diff --git a/blake2/src/simd/simdty.rs b/blake2/src/simd/simdty.rs
index 008b8b48c..3b271bd54 100644
--- a/blake2/src/simd/simdty.rs
+++ b/blake2/src/simd/simdty.rs
@@ -9,6 +9,9 @@
 
 use crate::as_bytes::Safe;
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::Zeroize;
+
 #[cfg(feature = "simd")]
 macro_rules! decl_simd {
     ($($decl:item)*) => {
@@ -50,6 +53,16 @@ decl_simd! {
                          pub T, pub T, pub T, pub T);
 }
 
+#[cfg(feature = "zeroize")]
+impl<T: Zeroize> Zeroize for Simd4<T> {
+    fn zeroize(&mut self) {
+        self.0.zeroize();
+        self.1.zeroize();
+        self.2.zeroize();
+        self.3.zeroize();
+    }
+}
+
 pub type u64x2 = Simd2<u64>;
 
 pub type u32x4 = Simd4<u32>;
diff --git a/fsb/Cargo.toml b/fsb/Cargo.toml
index e83ff8508..94b7b440f 100644
--- a/fsb/Cargo.toml
+++ b/fsb/Cargo.toml
@@ -23,3 +23,8 @@ hex-literal = "0.4"
 [features]
 default = ["std"]
 std = ["digest/std"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/fsb/src/lib.rs b/fsb/src/lib.rs
index 06760cb1a..141468fd7 100644
--- a/fsb/src/lib.rs
+++ b/fsb/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 #![allow(non_snake_case)]
@@ -27,6 +28,9 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 // FSB-160
 fsb_impl!(
     Fsb160,
diff --git a/fsb/src/macros.rs b/fsb/src/macros.rs
index 7b59004cd..2204b747c 100644
--- a/fsb/src/macros.rs
+++ b/fsb/src/macros.rs
@@ -12,6 +12,9 @@ macro_rules! fsb_impl {
             state: [u8; $r / 8],
         }
 
+        #[doc=$full_doc]
+        pub type $full_state = CoreWrapper<$state>;
+
         impl HashMarker for $state {}
 
         impl BlockSizeUser for $state {
@@ -79,8 +82,18 @@ macro_rules! fsb_impl {
             }
         }
 
-        #[doc=$full_doc]
-        pub type $full_state = CoreWrapper<$state>;
+        impl Drop for $state {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.state.zeroize();
+                    self.blocks_len.zeroize();
+                }
+            }
+        }
+
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $state {}
 
         impl $state {
             const SIZE_OUTPUT_COMPRESS: usize = $r / 8;
diff --git a/gost94/Cargo.toml b/gost94/Cargo.toml
index 8b485485a..ca7a4383a 100644
--- a/gost94/Cargo.toml
+++ b/gost94/Cargo.toml
@@ -23,3 +23,8 @@ hex-literal = "0.4"
 default = ["oid", "std"]
 std = ["digest/std"]
 oid = ["digest/oid"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/gost94/src/gost94_core.rs b/gost94/src/gost94_core.rs
index 671dae661..bc8c383a1 100644
--- a/gost94/src/gost94_core.rs
+++ b/gost94/src/gost94_core.rs
@@ -10,6 +10,9 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 use crate::params::{Block, Gost94Params, SBox};
 
 const C: Block = [
@@ -273,3 +276,17 @@ impl<P: Gost94Params> fmt::Debug for Gost94Core<P> {
         f.write_str("Core { .. }")
     }
 }
+
+impl<P: Gost94Params> Drop for Gost94Core<P> {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.h.zeroize();
+            self.n.zeroize();
+            self.sigma.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl<P: Gost94Params> ZeroizeOnDrop for Gost94Core<P> {}
diff --git a/gost94/src/lib.rs b/gost94/src/lib.rs
index bb4c04984..86a582528 100644
--- a/gost94/src/lib.rs
+++ b/gost94/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs, rust_2018_idioms)]
 #![forbid(unsafe_code)]
 
diff --git a/groestl/Cargo.toml b/groestl/Cargo.toml
index c8cf72fc4..70bdafdc9 100644
--- a/groestl/Cargo.toml
+++ b/groestl/Cargo.toml
@@ -22,3 +22,8 @@ hex-literal = "0.4"
 [features]
 default = ["std"]
 std = ["digest/std"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/groestl/src/lib.rs b/groestl/src/lib.rs
index a467c0ed7..dc7899d76 100644
--- a/groestl/src/lib.rs
+++ b/groestl/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(rust_2018_idioms)]
 
@@ -21,6 +22,9 @@ use digest::{
     HashMarker, InvalidOutputSize, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 mod compress1024;
 mod compress512;
 mod table;
@@ -32,6 +36,18 @@ pub struct GroestlShortVarCore {
     blocks_len: u64,
 }
 
+/// Short Groestl variant which allows to choose output size at runtime.
+pub type GroestlShortVar = RtVariableCoreWrapper<GroestlShortVarCore>;
+/// Core hasher state of the short Groestl variant generic over output size.
+pub type GroestlShortCore<OutSize> = CtVariableCoreWrapper<GroestlShortVarCore, OutSize>;
+/// Hasher state of the short Groestl variant generic over output size.
+pub type GroestlShort<OutSize> = CoreWrapper<GroestlShortCore<OutSize>>;
+
+/// Groestl-224 hasher state.
+pub type Groestl224 = CoreWrapper<GroestlShortCore<U28>>;
+/// Groestl-256 hasher state.
+pub type Groestl256 = CoreWrapper<GroestlShortCore<U32>>;
+
 impl HashMarker for GroestlShortVarCore {}
 
 impl BlockSizeUser for GroestlShortVarCore {
@@ -101,17 +117,18 @@ impl fmt::Debug for GroestlShortVarCore {
     }
 }
 
-/// Short Groestl variant which allows to choose output size at runtime.
-pub type GroestlShortVar = RtVariableCoreWrapper<GroestlShortVarCore>;
-/// Core hasher state of the short Groestl variant generic over output size.
-pub type GroestlShortCore<OutSize> = CtVariableCoreWrapper<GroestlShortVarCore, OutSize>;
-/// Hasher state of the short Groestl variant generic over output size.
-pub type GroestlShort<OutSize> = CoreWrapper<GroestlShortCore<OutSize>>;
+impl Drop for GroestlShortVarCore {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.state.zeroize();
+            self.blocks_len.zeroize();
+        }
+    }
+}
 
-/// Groestl-224 hasher state.
-pub type Groestl224 = CoreWrapper<GroestlShortCore<U28>>;
-/// Groestl-256 hasher state.
-pub type Groestl256 = CoreWrapper<GroestlShortCore<U32>>;
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for GroestlShortVarCore {}
 
 /// Lowest-level core hasher state of the long Groestl variant.
 #[derive(Clone)]
@@ -120,6 +137,18 @@ pub struct GroestlLongVarCore {
     blocks_len: u64,
 }
 
+/// Long Groestl variant which allows to choose output size at runtime.
+pub type GroestlLongVar = RtVariableCoreWrapper<GroestlLongVarCore>;
+/// Core hasher state of the long Groestl variant generic over output size.
+pub type GroestlLongCore<OutSize> = CtVariableCoreWrapper<GroestlLongVarCore, OutSize>;
+/// Hasher state of the long Groestl variant generic over output size.
+pub type GroestlLong<OutSize> = CoreWrapper<GroestlLongCore<OutSize>>;
+
+/// Groestl-384 hasher state.
+pub type Groestl384 = CoreWrapper<GroestlLongCore<U48>>;
+/// Groestl-512 hasher state.
+pub type Groestl512 = CoreWrapper<GroestlLongCore<U64>>;
+
 impl HashMarker for GroestlLongVarCore {}
 
 impl BlockSizeUser for GroestlLongVarCore {
@@ -189,14 +218,15 @@ impl fmt::Debug for GroestlLongVarCore {
     }
 }
 
-/// Long Groestl variant which allows to choose output size at runtime.
-pub type GroestlLongVar = RtVariableCoreWrapper<GroestlLongVarCore>;
-/// Core hasher state of the long Groestl variant generic over output size.
-pub type GroestlLongCore<OutSize> = CtVariableCoreWrapper<GroestlLongVarCore, OutSize>;
-/// Hasher state of the long Groestl variant generic over output size.
-pub type GroestlLong<OutSize> = CoreWrapper<GroestlLongCore<OutSize>>;
+impl Drop for GroestlLongVarCore {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.state.zeroize();
+            self.blocks_len.zeroize();
+        }
+    }
+}
 
-/// Groestl-384 hasher state.
-pub type Groestl384 = CoreWrapper<GroestlLongCore<U48>>;
-/// Groestl-512 hasher state.
-pub type Groestl512 = CoreWrapper<GroestlLongCore<U64>>;
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for GroestlLongVarCore {}
diff --git a/jh/Cargo.toml b/jh/Cargo.toml
index 5484082ca..f3122b13a 100644
--- a/jh/Cargo.toml
+++ b/jh/Cargo.toml
@@ -19,3 +19,10 @@ simd = { package = "ppv-lite86", version = "0.2.6" }
 
 [dev-dependencies]
 digest = { version = "=0.11.0-pre.6", features = ["dev"] }
+
+[features]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/jh/src/lib.rs b/jh/src/lib.rs
index cfae16f1a..44da3b16e 100644
--- a/jh/src/lib.rs
+++ b/jh/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs, rust_2018_idioms)]
 
 mod compressor;
@@ -27,6 +28,9 @@ use digest::{
     HashMarker, InvalidOutputSize, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 /// Core JH hasher state
 #[derive(Clone)]
 pub struct JhCore {
@@ -34,6 +38,15 @@ pub struct JhCore {
     block_len: u64,
 }
 
+/// Jh-224 hasher state
+pub type Jh224 = CoreWrapper<CtVariableCoreWrapper<JhCore, U28>>;
+/// Jh-256 hasher state
+pub type Jh256 = CoreWrapper<CtVariableCoreWrapper<JhCore, U32>>;
+/// Jh-384 hasher state
+pub type Jh384 = CoreWrapper<CtVariableCoreWrapper<JhCore, U48>>;
+/// Jh-512 hasher state
+pub type Jh512 = CoreWrapper<CtVariableCoreWrapper<JhCore, U64>>;
+
 impl HashMarker for JhCore {}
 
 impl BlockSizeUser for JhCore {
@@ -105,11 +118,20 @@ impl fmt::Debug for JhCore {
     }
 }
 
-/// Jh-224 hasher state
-pub type Jh224 = CoreWrapper<CtVariableCoreWrapper<JhCore, U28>>;
-/// Jh-256 hasher state
-pub type Jh256 = CoreWrapper<CtVariableCoreWrapper<JhCore, U32>>;
-/// Jh-384 hasher state
-pub type Jh384 = CoreWrapper<CtVariableCoreWrapper<JhCore, U48>>;
-/// Jh-512 hasher state
-pub type Jh512 = CoreWrapper<CtVariableCoreWrapper<JhCore, U64>>;
+impl Drop for JhCore {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            const N: usize = core::mem::size_of::<Compressor>();
+            // TODO: remove this unsafe after migration from `ppv-lite86`
+            unsafe {
+                let p: *mut [u8; N] = (&mut self.state as *mut Compressor).cast();
+                core::ptr::write_volatile(p, [0u8; N]);
+            }
+            self.block_len.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for JhCore {}
diff --git a/k12/Cargo.toml b/k12/Cargo.toml
index a09c95a58..9e300304e 100644
--- a/k12/Cargo.toml
+++ b/k12/Cargo.toml
@@ -23,3 +23,8 @@ hex-literal = "0.4"
 [features]
 default = ["std"]
 std = ["digest/std"]
+zeroize = ["digest/zeroize", "sha3/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/k12/src/lib.rs b/k12/src/lib.rs
index 129da40b3..dbebdade4 100644
--- a/k12/src/lib.rs
+++ b/k12/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
@@ -17,9 +18,11 @@ use digest::core_api::{
     UpdateCore, XofReaderCore, XofReaderCoreWrapper,
 };
 use digest::{ExtendableOutputReset, HashMarker, Reset, Update, XofReader};
-
 use sha3::{TurboShake128, TurboShake128Core, TurboShake128ReaderCore};
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 const CHUNK_SIZE: usize = 8192;
 const CHAINING_VALUE_SIZE: usize = 32;
 const LENGTH_ENCODE_SIZE: usize = 255;
@@ -36,6 +39,9 @@ pub struct KangarooTwelveCore<'cs> {
     chain_length: usize,
 }
 
+/// [`KangarooTwelve`] hasher state.
+pub type KangarooTwelve<'cs> = CoreWrapper<KangarooTwelveCore<'cs>>;
+
 impl<'cs> KangarooTwelveCore<'cs> {
     /// Creates a new KangarooTwelve instance with the given customization.
     pub fn new(customization: &'cs [u8]) -> Self {
@@ -179,6 +185,21 @@ impl fmt::Debug for KangarooTwelveCore<'_> {
     }
 }
 
+impl Drop for KangarooTwelveCore<'_> {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.buffer.zeroize();
+            self.bufpos.zeroize();
+            self.chain_length.zeroize();
+            // final_tshk and chain_tshk zeroized by their Drop impl
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for KangarooTwelveCore<'_> {}
+
 /// Core [`KangarooTwelve`] reader state.
 #[derive(Clone)]
 #[allow(non_camel_case_types)]
@@ -186,6 +207,9 @@ pub struct KangarooTwelveReaderCore {
     tshk: XofReaderCoreWrapper<TurboShake128ReaderCore>,
 }
 
+/// [`KangarooTwelve`] reader state.
+pub type KangarooTwelveReader = XofReaderCoreWrapper<KangarooTwelveReaderCore>;
+
 impl BlockSizeUser for KangarooTwelveReaderCore {
     type BlockSize = U168; // TurboSHAKE128 block size
 }
@@ -199,11 +223,9 @@ impl XofReaderCore for KangarooTwelveReaderCore {
     }
 }
 
-/// [`KangarooTwelve`] hasher state.
-pub type KangarooTwelve<'cs> = CoreWrapper<KangarooTwelveCore<'cs>>;
-
-/// [`KangarooTwelve`] reader state.
-pub type KangarooTwelveReader = XofReaderCoreWrapper<KangarooTwelveReaderCore>;
+// `TurboShake128ReaderCore` and the wrapper are zeroized by their Drop impls
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for KangarooTwelveReaderCore {}
 
 fn length_encode(mut length: usize, buffer: &mut [u8; LENGTH_ENCODE_SIZE]) -> &mut [u8] {
     let mut bufpos = 0usize;
diff --git a/md2/Cargo.toml b/md2/Cargo.toml
index f3611b0bd..6691cf1e7 100644
--- a/md2/Cargo.toml
+++ b/md2/Cargo.toml
@@ -23,3 +23,8 @@ hex-literal = "0.4"
 default = ["oid", "std"]
 std = ["digest/std"]
 oid = ["digest/oid"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/md2/src/lib.rs b/md2/src/lib.rs
index 76c5a5564..fde870a3e 100644
--- a/md2/src/lib.rs
+++ b/md2/src/lib.rs
@@ -4,14 +4,13 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
 pub use digest::{self, Digest};
 
 use core::fmt;
-#[cfg(feature = "oid")]
-use digest::const_oid::{AssociatedOid, ObjectIdentifier};
 use digest::{
     block_buffer::Eager,
     consts::U16,
@@ -22,6 +21,11 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "oid")]
+use digest::const_oid::{AssociatedOid, ObjectIdentifier};
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 mod consts;
 
 /// Core MD2 hasher state.
@@ -31,6 +35,9 @@ pub struct Md2Core {
     checksum: Block<Self>,
 }
 
+/// MD2 hasher state.
+pub type Md2 = CoreWrapper<Md2Core>;
+
 impl Md2Core {
     fn compress(&mut self, block: &Block<Self>) {
         // Update state
@@ -130,5 +137,15 @@ impl AssociatedOid for Md2Core {
     const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.2.2");
 }
 
-/// MD2 hasher state.
-pub type Md2 = CoreWrapper<Md2Core>;
+impl Drop for Md2Core {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.x.zeroize();
+            self.checksum.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for Md2Core {}
diff --git a/md4/Cargo.toml b/md4/Cargo.toml
index d4d73d2d5..f1a640a46 100644
--- a/md4/Cargo.toml
+++ b/md4/Cargo.toml
@@ -23,3 +23,8 @@ hex-literal = "0.4"
 default = ["oid", "std"]
 std = ["digest/std"]
 oid = ["digest/oid"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/md4/src/lib.rs b/md4/src/lib.rs
index 24d2f6bc8..04d04749c 100644
--- a/md4/src/lib.rs
+++ b/md4/src/lib.rs
@@ -4,14 +4,13 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(rust_2018_idioms)]
 
 pub use digest::{self, Digest};
 
 use core::{fmt, num::Wrapping as W};
-#[cfg(feature = "oid")]
-use digest::const_oid::{AssociatedOid, ObjectIdentifier};
 use digest::{
     block_buffer::Eager,
     core_api::{
@@ -22,6 +21,11 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "oid")]
+use digest::const_oid::{AssociatedOid, ObjectIdentifier};
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 type Wu32 = W<u32>;
 const S0: [Wu32; 4] = [
     W(0x6745_2301),
@@ -32,12 +36,16 @@ const S0: [Wu32; 4] = [
 const K1: Wu32 = W(0x5A82_7999);
 const K2: Wu32 = W(0x6ED9_EBA1);
 
+/// MD4 core hasher state
 #[derive(Clone)]
 pub struct Md4Core {
     block_len: W<u64>,
     state: [Wu32; 4],
 }
 
+/// MD4 hasher state
+pub type Md4 = CoreWrapper<Md4Core>;
+
 impl HashMarker for Md4Core {}
 
 impl BlockSizeUser for Md4Core {
@@ -113,8 +121,18 @@ impl AssociatedOid for Md4Core {
     const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.2.4");
 }
 
-/// MD4 hasher state.
-pub type Md4 = CoreWrapper<Md4Core>;
+impl Drop for Md4Core {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.state.zeroize();
+            self.block_len.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for Md4Core {}
 
 fn compress(state: &mut [Wu32; 4], input: &Block<Md4Core>) {
     fn f(x: Wu32, y: Wu32, z: Wu32) -> Wu32 {
diff --git a/md5/Cargo.toml b/md5/Cargo.toml
index c0e943299..7f458c9f3 100644
--- a/md5/Cargo.toml
+++ b/md5/Cargo.toml
@@ -27,4 +27,9 @@ hex-literal = "0.4"
 default = ["oid", "std"]
 std = ["digest/std"]
 oid = ["digest/oid"] # Enable OID support
+zeroize = ["digest/zeroize"]
 force-soft = [] # Force software implementation
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/md5/src/lib.rs b/md5/src/lib.rs
index 2930b5afb..d5d1db107 100644
--- a/md5/src/lib.rs
+++ b/md5/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs, rust_2018_idioms)]
 
 pub use digest::{self, Digest};
@@ -12,8 +13,6 @@ mod compress;
 pub(crate) mod consts;
 
 use core::{fmt, slice::from_ref};
-#[cfg(feature = "oid")]
-use digest::const_oid::{AssociatedOid, ObjectIdentifier};
 use digest::{
     array::ArrayOps,
     block_buffer::Eager,
@@ -25,6 +24,11 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "oid")]
+use digest::const_oid::{AssociatedOid, ObjectIdentifier};
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 /// Core MD5 hasher state.
 #[derive(Clone)]
 pub struct Md5Core {
@@ -32,6 +36,9 @@ pub struct Md5Core {
     state: [u32; 4],
 }
 
+/// MD5 hasher state.
+pub type Md5 = CoreWrapper<Md5Core>;
+
 impl HashMarker for Md5Core {}
 
 impl BlockSizeUser for Md5Core {
@@ -106,5 +113,15 @@ impl AssociatedOid for Md5Core {
     const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.2.5");
 }
 
-/// MD5 hasher state.
-pub type Md5 = CoreWrapper<Md5Core>;
+impl Drop for Md5Core {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.state.zeroize();
+            self.block_len.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for Md5Core {}
diff --git a/ripemd/Cargo.toml b/ripemd/Cargo.toml
index 7f5377eb8..ef115a5c2 100644
--- a/ripemd/Cargo.toml
+++ b/ripemd/Cargo.toml
@@ -23,3 +23,8 @@ hex-literal = "0.4"
 default = ["oid", "std"]
 std = ["digest/std"]
 oid = ["digest/oid"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/ripemd/src/lib.rs b/ripemd/src/lib.rs
index eb0d04167..0dfa357bc 100644
--- a/ripemd/src/lib.rs
+++ b/ripemd/src/lib.rs
@@ -4,14 +4,13 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
 pub use digest::{self, Digest};
 
 use core::fmt;
-#[cfg(feature = "oid")]
-use digest::const_oid::{AssociatedOid, ObjectIdentifier};
 use digest::{
     block_buffer::Eager,
     core_api::{
@@ -22,6 +21,11 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "oid")]
+use digest::const_oid::{AssociatedOid, ObjectIdentifier};
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 mod c128;
 mod c160;
 mod c256;
@@ -41,6 +45,10 @@ macro_rules! impl_ripemd {
             block_len: u64,
         }
 
+        #[doc = $doc_name]
+        #[doc = " hasher."]
+        pub type $wrapped_name = CoreWrapper<$name>;
+
         impl HashMarker for $name {}
 
         impl BlockSizeUser for $name {
@@ -111,9 +119,18 @@ macro_rules! impl_ripemd {
             }
         }
 
-        #[doc = $doc_name]
-        #[doc = " hasher."]
-        pub type $wrapped_name = CoreWrapper<$name>;
+        impl Drop for $name {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.h.zeroize();
+                    self.block_len.zeroize();
+                }
+            }
+        }
+
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $name {}
     };
 }
 
diff --git a/sha1/Cargo.toml b/sha1/Cargo.toml
index edfe709c7..069a32025 100644
--- a/sha1/Cargo.toml
+++ b/sha1/Cargo.toml
@@ -27,6 +27,7 @@ hex-literal = "0.4"
 default = ["oid", "std"]
 std = ["digest/std"]
 oid = ["digest/oid"] # Enable OID support
+zeroize = ["digest/zeroize"]
 force-soft = [] # Force software implementation
 
 [package.metadata.docs.rs]
diff --git a/sha1/src/lib.rs b/sha1/src/lib.rs
index 6cf035fc4..6ad713f03 100644
--- a/sha1/src/lib.rs
+++ b/sha1/src/lib.rs
@@ -1,17 +1,15 @@
 #![no_std]
 #![doc = include_str!("../README.md")]
-#![cfg_attr(docsrs, feature(doc_cfg))]
 #![doc(
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs, rust_2018_idioms)]
 
 pub use digest::{self, Digest};
 
 use core::{fmt, slice::from_ref};
-#[cfg(feature = "oid")]
-use digest::const_oid::{AssociatedOid, ObjectIdentifier};
 use digest::{
     array::ArrayOps,
     block_buffer::Eager,
@@ -23,6 +21,11 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "oid")]
+use digest::const_oid::{AssociatedOid, ObjectIdentifier};
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 mod compress;
 
 pub use compress::compress;
@@ -37,6 +40,9 @@ pub struct Sha1Core {
     block_len: u64,
 }
 
+/// SHA-1 hasher state.
+pub type Sha1 = CoreWrapper<Sha1Core>;
+
 impl HashMarker for Sha1Core {}
 
 impl BlockSizeUser for Sha1Core {
@@ -109,5 +115,15 @@ impl AssociatedOid for Sha1Core {
     const OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.14.3.2.26");
 }
 
-/// SHA-1 hasher state.
-pub type Sha1 = CoreWrapper<Sha1Core>;
+impl Drop for Sha1Core {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.h.zeroize();
+            self.block_len.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for Sha1Core {}
diff --git a/sha2/Cargo.toml b/sha2/Cargo.toml
index 1ab025950..a5e82db1b 100644
--- a/sha2/Cargo.toml
+++ b/sha2/Cargo.toml
@@ -30,6 +30,7 @@ hex-literal = "0.4"
 default = ["oid", "std"]
 std = ["digest/std"]
 oid = ["digest/oid"] # Enable OID support
+zeroize = ["digest/zeroize"]
 force-soft = [] # Force software implementation
 
 [package.metadata.docs.rs]
diff --git a/sha2/src/core_api.rs b/sha2/src/core_api.rs
index d492a79ac..d9aff2896 100644
--- a/sha2/src/core_api.rs
+++ b/sha2/src/core_api.rs
@@ -11,6 +11,9 @@ use digest::{
     HashMarker, InvalidOutputSize, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 /// Core block-level SHA-256 hasher with variable output size.
 ///
 /// Supports initialization only for 28 and 32 byte output sizes,
@@ -84,6 +87,19 @@ impl fmt::Debug for Sha256VarCore {
     }
 }
 
+impl Drop for Sha256VarCore {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.state.zeroize();
+            self.block_len.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for Sha256VarCore {}
+
 /// Core block-level SHA-512 hasher with variable output size.
 ///
 /// Supports initialization only for 28, 32, 48, and 64 byte output sizes,
@@ -158,3 +174,15 @@ impl fmt::Debug for Sha512VarCore {
         f.write_str("Sha512VarCore { ... }")
     }
 }
+
+impl Drop for Sha512VarCore {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.state.zeroize();
+            self.block_len.zeroize();
+        }
+    }
+}
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for Sha512VarCore {}
diff --git a/sha2/src/lib.rs b/sha2/src/lib.rs
index 973851e5d..f6eae82a0 100644
--- a/sha2/src/lib.rs
+++ b/sha2/src/lib.rs
@@ -1,10 +1,10 @@
 #![no_std]
 #![doc = include_str!("../README.md")]
-#![cfg_attr(docsrs, feature(doc_cfg))]
 #![doc(
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs, rust_2018_idioms)]
 
 pub use digest::{self, Digest};
diff --git a/sha3/Cargo.toml b/sha3/Cargo.toml
index 91031b7a3..0c6091681 100644
--- a/sha3/Cargo.toml
+++ b/sha3/Cargo.toml
@@ -19,7 +19,6 @@ rust-version = "1.71"
 [dependencies]
 digest = "=0.11.0-pre.6"
 keccak = "0.1.4"
-zeroize = { version = "1.6.0", default-features = false, optional=true } # WARNING: Bumps MSRV to 1.56
 
 [dev-dependencies]
 digest = { version = "=0.11.0-pre.6", features = ["dev"] }
@@ -28,7 +27,11 @@ hex-literal = "0.4"
 [features]
 default = ["oid", "std"]
 std = ["digest/std"]
-
-asm = ["keccak/asm"] # Enable ASM (currently ARMv8 only).
 oid = ["digest/oid"] # Enable OID support.
+zeroize = ["digest/zeroize"]
+asm = ["keccak/asm"] # Enable ASM (currently ARMv8 only).
 reset = [] # Enable reset functionality
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/sha3/src/lib.rs b/sha3/src/lib.rs
index d7fccecbf..68c519a09 100644
--- a/sha3/src/lib.rs
+++ b/sha3/src/lib.rs
@@ -4,14 +4,13 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
 pub use digest::{self, Digest};
 
 use core::fmt;
-#[cfg(feature = "oid")]
-use digest::const_oid::{AssociatedOid, ObjectIdentifier};
 use digest::{
     array::typenum::Unsigned,
     block_buffer::Eager,
@@ -24,6 +23,11 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "oid")]
+use digest::const_oid::{AssociatedOid, ObjectIdentifier};
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 #[macro_use]
 mod macros;
 mod state;
diff --git a/sha3/src/macros.rs b/sha3/src/macros.rs
index b7e346c32..639703119 100644
--- a/sha3/src/macros.rs
+++ b/sha3/src/macros.rs
@@ -12,6 +12,10 @@ macro_rules! impl_sha3 {
             state: Sha3State,
         }
 
+        #[doc = $alg_name]
+        #[doc = " hasher state."]
+        pub type $full_name = CoreWrapper<$name>;
+
         impl HashMarker for $name {}
 
         impl BlockSizeUser for $name {
@@ -78,9 +82,17 @@ macro_rules! impl_sha3 {
             }
         }
 
-        #[doc = $alg_name]
-        #[doc = " hasher state."]
-        pub type $full_name = CoreWrapper<$name>;
+        impl Drop for $name {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.state.state.zeroize();
+                }
+            }
+        }
+
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $name {}
     };
     (
         $name:ident, $full_name:ident, $output_size:ident,
@@ -110,6 +122,10 @@ macro_rules! impl_shake {
             state: Sha3State,
         }
 
+        #[doc = $alg_name]
+        #[doc = " hasher state."]
+        pub type $full_name = CoreWrapper<$name>;
+
         impl HashMarker for $name {}
 
         impl BlockSizeUser for $name {
@@ -175,6 +191,18 @@ macro_rules! impl_shake {
             }
         }
 
+        impl Drop for $name {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.state.state.zeroize();
+                }
+            }
+        }
+
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $name {}
+
         #[doc = "Core "]
         #[doc = $alg_name]
         #[doc = " reader state."]
@@ -184,6 +212,10 @@ macro_rules! impl_shake {
             state: Sha3State,
         }
 
+        #[doc = $alg_name]
+        #[doc = " reader state."]
+        pub type $reader_full = XofReaderCoreWrapper<$reader>;
+
         impl BlockSizeUser for $reader {
             type BlockSize = $rate;
         }
@@ -198,13 +230,17 @@ macro_rules! impl_shake {
             }
         }
 
-        #[doc = $alg_name]
-        #[doc = " hasher state."]
-        pub type $full_name = CoreWrapper<$name>;
+        impl Drop for $reader {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.state.state.zeroize();
+                }
+            }
+        }
 
-        #[doc = $alg_name]
-        #[doc = " reader state."]
-        pub type $reader_full = XofReaderCoreWrapper<$reader>;
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $reader {}
     };
     (
         $name:ident, $full_name:ident, $reader:ident, $reader_full:ident,
@@ -239,10 +275,14 @@ macro_rules! impl_turbo_shake {
         #[derive(Clone)]
         #[allow(non_camel_case_types)]
         pub struct $name {
+            state: Sha3State<TURBO_SHAKE_ROUND_COUNT>,
             domain_separation: u8,
-            state: Sha3State,
         }
 
+        #[doc = $alg_name]
+        #[doc = " hasher state."]
+        pub type $full_name = CoreWrapper<$name>;
+
         impl $name {
             /// Creates a new TurboSHAKE instance with the given domain separation.
             /// Note that the domain separation needs to be a byte with a value in
@@ -251,7 +291,7 @@ macro_rules! impl_turbo_shake {
                 assert!((0x01..=0x7F).contains(&domain_separation));
                 Self {
                     domain_separation,
-                    state: Sha3State::new(TURBO_SHAKE_ROUND_COUNT),
+                    state: Default::default(),
                 }
             }
         }
@@ -312,15 +352,32 @@ macro_rules! impl_turbo_shake {
             }
         }
 
+        impl Drop for $name {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.state.state.zeroize();
+                    self.domain_separation.zeroize();
+                }
+            }
+        }
+
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $name {}
+
         #[doc = "Core "]
         #[doc = $alg_name]
         #[doc = " reader state."]
         #[derive(Clone)]
         #[allow(non_camel_case_types)]
         pub struct $reader {
-            state: Sha3State,
+            state: Sha3State<TURBO_SHAKE_ROUND_COUNT>,
         }
 
+        #[doc = $alg_name]
+        #[doc = " reader state."]
+        pub type $reader_full = XofReaderCoreWrapper<$reader>;
+
         impl BlockSizeUser for $reader {
             type BlockSize = $rate;
         }
@@ -335,13 +392,17 @@ macro_rules! impl_turbo_shake {
             }
         }
 
-        #[doc = $alg_name]
-        #[doc = " hasher state."]
-        pub type $full_name = CoreWrapper<$name>;
+        impl Drop for $reader {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.state.state.zeroize();
+                }
+            }
+        }
 
-        #[doc = $alg_name]
-        #[doc = " reader state."]
-        pub type $reader_full = XofReaderCoreWrapper<$reader>;
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $reader {}
     };
     (
         $name:ident, $full_name:ident, $reader:ident, $reader_full:ident,
@@ -368,12 +429,16 @@ macro_rules! impl_cshake {
         #[derive(Clone)]
         #[allow(non_camel_case_types)]
         pub struct $name {
-            padding: u8,
             state: Sha3State,
             #[cfg(feature = "reset")]
             initial_state: Sha3State,
+            padding: u8,
         }
 
+        #[doc = $alg_name]
+        #[doc = " hasher state."]
+        pub type $full_name = CoreWrapper<$name>;
+
         impl $name {
             /// Creates a new CSHAKE instance with the given customization.
             pub fn new(customization: &[u8]) -> Self {
@@ -495,6 +560,21 @@ macro_rules! impl_cshake {
             }
         }
 
+        impl Drop for $name {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.state.state.zeroize();
+                    self.padding.zeroize();
+                    #[cfg(feature = "reset")]
+                    self.initial_state.state.zeroize();
+                }
+            }
+        }
+
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $name {}
+
         #[doc = "Core "]
         #[doc = $alg_name]
         #[doc = " reader state."]
@@ -504,6 +584,10 @@ macro_rules! impl_cshake {
             state: Sha3State,
         }
 
+        #[doc = $alg_name]
+        #[doc = " reader state."]
+        pub type $reader_full = XofReaderCoreWrapper<$reader>;
+
         impl BlockSizeUser for $reader {
             type BlockSize = $rate;
         }
@@ -518,12 +602,16 @@ macro_rules! impl_cshake {
             }
         }
 
-        #[doc = $alg_name]
-        #[doc = " hasher state."]
-        pub type $full_name = CoreWrapper<$name>;
+        impl Drop for $reader {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.state.state.zeroize();
+                }
+            }
+        }
 
-        #[doc = $alg_name]
-        #[doc = " reader state."]
-        pub type $reader_full = XofReaderCoreWrapper<$reader>;
+        #[cfg(feature = "zeroize")]
+        impl ZeroizeOnDrop for $reader {}
     };
 }
diff --git a/sha3/src/state.rs b/sha3/src/state.rs
index b651cd3e8..351ffccf4 100644
--- a/sha3/src/state.rs
+++ b/sha3/src/state.rs
@@ -1,62 +1,37 @@
-#[cfg(feature = "zeroize")]
-use zeroize::{Zeroize, ZeroizeOnDrop};
-
 const PLEN: usize = 25;
 const DEFAULT_ROUND_COUNT: usize = 24;
 
 #[derive(Clone)]
-pub(crate) struct Sha3State {
+pub(crate) struct Sha3State<const ROUNDS: usize = DEFAULT_ROUND_COUNT> {
     pub state: [u64; PLEN],
-    round_count: usize,
 }
 
-impl Default for Sha3State {
+impl<const ROUNDS: usize> Default for Sha3State<ROUNDS> {
     fn default() -> Self {
         Self {
             state: [0u64; PLEN],
-            round_count: DEFAULT_ROUND_COUNT,
         }
     }
 }
 
-#[cfg(feature = "zeroize")]
-impl Drop for Sha3State {
-    fn drop(&mut self) {
-        self.state.zeroize();
-    }
-}
-
-#[cfg(feature = "zeroize")]
-impl ZeroizeOnDrop for Sha3State {}
-
-impl Sha3State {
-    pub(crate) fn new(round_count: usize) -> Self {
-        Self {
-            state: [0u64; PLEN],
-            round_count,
-        }
-    }
-
-    #[inline(always)]
-    pub(crate) fn absorb_block(&mut self, block: &[u8]) {
+impl<const ROUNDS: usize> Sha3State<ROUNDS> {
+    pub fn absorb_block(&mut self, block: &[u8]) {
         debug_assert_eq!(block.len() % 8, 0);
 
         for (b, s) in block.chunks_exact(8).zip(self.state.iter_mut()) {
             *s ^= u64::from_le_bytes(b.try_into().unwrap());
         }
 
-        keccak::p1600(&mut self.state, self.round_count);
+        keccak::p1600(&mut self.state, ROUNDS);
     }
 
-    #[inline(always)]
-    pub(crate) fn as_bytes(&self, out: &mut [u8]) {
+    pub fn as_bytes(&self, out: &mut [u8]) {
         for (o, s) in out.chunks_mut(8).zip(self.state.iter()) {
             o.copy_from_slice(&s.to_le_bytes()[..o.len()]);
         }
     }
 
-    #[inline(always)]
-    pub(crate) fn permute(&mut self) {
-        keccak::p1600(&mut self.state, self.round_count);
+    pub fn permute(&mut self) {
+        keccak::p1600(&mut self.state, ROUNDS);
     }
 }
diff --git a/shabal/Cargo.toml b/shabal/Cargo.toml
index 5b4d46810..e1d6ad764 100644
--- a/shabal/Cargo.toml
+++ b/shabal/Cargo.toml
@@ -22,3 +22,8 @@ hex-literal = "0.4"
 [features]
 default = ["std"]
 std = ["digest/std"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/shabal/src/core_api.rs b/shabal/src/core_api.rs
index dc174268f..2ee07ec35 100644
--- a/shabal/src/core_api.rs
+++ b/shabal/src/core_api.rs
@@ -11,6 +11,9 @@ use digest::{
     HashMarker, InvalidOutputSize, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 type BlockSize = U64;
 type Block = Array<u8, BlockSize>;
 type M = [Wrapping<u32>; 16];
@@ -241,3 +244,18 @@ impl fmt::Debug for ShabalVarCore {
         f.write_str("ShabalVarCore { ... }")
     }
 }
+
+impl Drop for ShabalVarCore {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.a.zeroize();
+            self.b.zeroize();
+            self.c.zeroize();
+            self.w.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for ShabalVarCore {}
diff --git a/shabal/src/lib.rs b/shabal/src/lib.rs
index fbfe2da16..83161bebf 100644
--- a/shabal/src/lib.rs
+++ b/shabal/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
diff --git a/skein/Cargo.toml b/skein/Cargo.toml
index e1cb38506..5b3127200 100644
--- a/skein/Cargo.toml
+++ b/skein/Cargo.toml
@@ -19,3 +19,12 @@ threefish = { version = "0.5.2", default-features = false }
 [dev-dependencies]
 digest = { version = "=0.11.0-pre.6", features = ["dev"] }
 hex-literal = "0.4"
+
+[features]
+default = ["std"]
+std = ["digest/std"]
+zeroize = ["digest/zeroize", "threefish/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/skein/src/lib.rs b/skein/src/lib.rs
index 649ea8a95..7595cba26 100644
--- a/skein/src/lib.rs
+++ b/skein/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs, rust_2018_idioms)]
 #![deny(unsafe_code)]
 
@@ -22,6 +23,9 @@ use digest::{
 };
 use threefish::{Threefish1024, Threefish256, Threefish512};
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 const VERSION: u64 = 1;
 const ID_STRING_LE: u64 = 0x3341_4853;
 const SCHEMA_VER: u64 = (VERSION << 32) | ID_STRING_LE;
@@ -47,6 +51,10 @@ macro_rules! define_hasher {
             _pd: PhantomData<N>,
         }
 
+        #[doc = $alg_name]
+        #[doc = " hasher state"]
+        pub type $full_name<OutputSize = $state_bytes> = CoreWrapper<$name<OutputSize>>;
+
         impl<N: ArraySize + 'static> $name<N> {
             fn blank_state(t1: u64, x: [u64; <$state_bytes>::USIZE / 8]) -> Self {
                 Self {
@@ -165,9 +173,18 @@ macro_rules! define_hasher {
             }
         }
 
-        #[doc = $alg_name]
-        #[doc = " hasher state"]
-        pub type $full_name<OutputSize = $state_bytes> = CoreWrapper<$name<OutputSize>>;
+        impl<N: ArraySize + 'static> Drop for $name<N> {
+            fn drop(&mut self) {
+                #[cfg(feature = "zeroize")]
+                {
+                    self.t.zeroize();
+                    self.x.zeroize();
+                }
+            }
+        }
+
+        #[cfg(feature = "zeroize")]
+        impl<N: ArraySize + 'static> ZeroizeOnDrop for $name<N> {}
     };
 }
 
diff --git a/sm3/Cargo.toml b/sm3/Cargo.toml
index 126b4373b..59d00f126 100644
--- a/sm3/Cargo.toml
+++ b/sm3/Cargo.toml
@@ -22,3 +22,8 @@ hex-literal = "0.4"
 [features]
 default = ["std"]
 std = ["digest/std"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/sm3/src/lib.rs b/sm3/src/lib.rs
index 0452dc1bc..f0a31a814 100644
--- a/sm3/src/lib.rs
+++ b/sm3/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
@@ -20,6 +21,9 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 mod compress;
 mod consts;
 
@@ -32,6 +36,9 @@ pub struct Sm3Core {
     h: [u32; 8],
 }
 
+/// Sm3 hasher state.
+pub type Sm3 = CoreWrapper<Sm3Core>;
+
 impl HashMarker for Sm3Core {}
 
 impl BlockSizeUser for Sm3Core {
@@ -97,5 +104,15 @@ impl fmt::Debug for Sm3Core {
     }
 }
 
-/// Sm3 hasher state.
-pub type Sm3 = CoreWrapper<Sm3Core>;
+impl Drop for Sm3Core {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.h.zeroize();
+            self.block_len.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for Sm3Core {}
diff --git a/streebog/Cargo.toml b/streebog/Cargo.toml
index 7c81c8b40..9da8473b3 100644
--- a/streebog/Cargo.toml
+++ b/streebog/Cargo.toml
@@ -23,3 +23,8 @@ hex-literal = "0.4"
 default = ["oid", "std"]
 std = ["digest/std"]
 oid = ["digest/oid"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/streebog/src/core_api.rs b/streebog/src/core_api.rs
index 50adfc1b5..826a8a145 100644
--- a/streebog/src/core_api.rs
+++ b/streebog/src/core_api.rs
@@ -9,6 +9,9 @@ use digest::{
     HashMarker, InvalidOutputSize, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 use crate::consts::{BLOCK_SIZE, C, SHUFFLED_LIN_TABLE};
 
 type Block = [u8; 64];
@@ -162,6 +165,20 @@ impl fmt::Debug for StreebogVarCore {
     }
 }
 
+impl Drop for StreebogVarCore {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.h.zeroize();
+            self.n.zeroize();
+            self.sigma.zeroize();
+        }
+    }
+}
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for StreebogVarCore {}
+
 #[inline(always)]
 fn adc(a: &mut u64, b: u64, carry: &mut u64) {
     let ret = (*a as u128) + (b as u128) + (*carry as u128);
diff --git a/streebog/src/lib.rs b/streebog/src/lib.rs
index 26ab42235..e337fc3ea 100644
--- a/streebog/src/lib.rs
+++ b/streebog/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![forbid(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
diff --git a/tiger/Cargo.toml b/tiger/Cargo.toml
index d81f1ceae..fbaa1952b 100644
--- a/tiger/Cargo.toml
+++ b/tiger/Cargo.toml
@@ -10,7 +10,7 @@ documentation = "https://docs.rs/tiger"
 repository = "https://github.com/RustCrypto/hashes"
 keywords = ["crypto", "hash", "tiger", "digest"]
 categories = ["cryptography", "no-std"]
-rust-version = "1.71"
+rust-version = "1.74"
 
 [dependencies]
 digest = "=0.11.0-pre.6"
@@ -22,3 +22,8 @@ hex-literal = "0.4"
 [features]
 default = ["std"]
 std = ["digest/std"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/tiger/README.md b/tiger/README.md
index c88d69f12..a0c084477 100644
--- a/tiger/README.md
+++ b/tiger/README.md
@@ -28,7 +28,7 @@ Also, see the [examples section] in the RustCrypto/hashes readme.
 
 ## Minimum Supported Rust Version
 
-Rust **1.71** or higher.
+Rust **1.74** or higher.
 
 Minimum supported Rust version can be changed in the future, but it will be
 done with a minor version bump.
@@ -59,7 +59,7 @@ for inclusion in the work by you, as defined in the Apache-2.0 license, without
 [docs-image]: https://docs.rs/tiger/badge.svg
 [docs-link]: https://docs.rs/tiger/
 [license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg
-[rustc-image]: https://img.shields.io/badge/rustc-1.71+-blue.svg
+[rustc-image]: https://img.shields.io/badge/rustc-1.74+-blue.svg
 [chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg
 [chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260041-hashes
 [build-image]: https://github.com/RustCrypto/hashes/workflows/tiger/badge.svg?branch=master
diff --git a/tiger/src/compress.rs b/tiger/src/compress.rs
index 3ee461b1b..8ac864050 100644
--- a/tiger/src/compress.rs
+++ b/tiger/src/compress.rs
@@ -1,18 +1,18 @@
-use super::tables::{T1, T2, T3, T4};
+use super::tables::TABLES;
 use super::State;
 
 #[inline(always)]
 fn round(a: &mut u64, b: &mut u64, c: &mut u64, x: &u64, mul: u8) {
     *c ^= *x;
     let c2: [u8; 8] = c.to_le_bytes();
-    let a2 = T1[usize::from(c2[0])]
-        ^ T2[usize::from(c2[2])]
-        ^ T3[usize::from(c2[4])]
-        ^ T4[usize::from(c2[6])];
-    let b2 = T4[usize::from(c2[1])]
-        ^ T3[usize::from(c2[3])]
-        ^ T2[usize::from(c2[5])]
-        ^ T1[usize::from(c2[7])];
+    let a2 = TABLES[0][usize::from(c2[0])]
+        ^ TABLES[1][usize::from(c2[2])]
+        ^ TABLES[2][usize::from(c2[4])]
+        ^ TABLES[3][usize::from(c2[6])];
+    let b2 = TABLES[3][usize::from(c2[1])]
+        ^ TABLES[2][usize::from(c2[3])]
+        ^ TABLES[1][usize::from(c2[5])]
+        ^ TABLES[0][usize::from(c2[7])];
     *a = a.wrapping_sub(a2);
     *b = b.wrapping_add(b2).wrapping_mul(u64::from(mul));
 }
diff --git a/tiger/src/lib.rs b/tiger/src/lib.rs
index 32690d977..c92e3636e 100644
--- a/tiger/src/lib.rs
+++ b/tiger/src/lib.rs
@@ -4,7 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
-#![forbid(unsafe_code)]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![warn(missing_docs, rust_2018_idioms)]
 
 pub use digest::{self, Digest};
@@ -20,6 +20,9 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 mod compress;
 mod tables;
 use compress::compress;
@@ -33,26 +36,31 @@ const S0: State = [
 
 /// Core Tiger hasher state.
 #[derive(Clone)]
-pub struct TigerCore {
+pub struct TigerCore<const VER2: bool = true> {
     block_len: u64,
     state: State,
 }
 
-impl HashMarker for TigerCore {}
+/// Tiger hasher state.
+pub type Tiger = CoreWrapper<TigerCore<false>>;
+/// Tiger2 hasher state.
+pub type Tiger2 = CoreWrapper<TigerCore<true>>;
+
+impl<const VER2: bool> HashMarker for TigerCore<VER2> {}
 
-impl BlockSizeUser for TigerCore {
+impl<const VER2: bool> BlockSizeUser for TigerCore<VER2> {
     type BlockSize = U64;
 }
 
-impl BufferKindUser for TigerCore {
+impl<const VER2: bool> BufferKindUser for TigerCore<VER2> {
     type BufferKind = Eager;
 }
 
-impl OutputSizeUser for TigerCore {
+impl<const VER2: bool> OutputSizeUser for TigerCore<VER2> {
     type OutputSize = U24;
 }
 
-impl UpdateCore for TigerCore {
+impl<const VER2: bool> UpdateCore for TigerCore<VER2> {
     #[inline]
     fn update_blocks(&mut self, blocks: &[Block<Self>]) {
         self.block_len += blocks.len() as u64;
@@ -62,23 +70,29 @@ impl UpdateCore for TigerCore {
     }
 }
 
-impl FixedOutputCore for TigerCore {
+impl<const VER2: bool> FixedOutputCore for TigerCore<VER2> {
     #[inline]
     fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
         let bs = Self::BlockSize::U64;
         let pos = buffer.get_pos() as u64;
         let bit_len = 8 * (pos + bs * self.block_len);
 
-        buffer.digest_pad(1, &bit_len.to_le_bytes(), |b| {
-            compress(&mut self.state, b.as_ref())
-        });
+        if VER2 {
+            buffer.len64_padding_le(bit_len, |b| compress(&mut self.state, b.as_ref()));
+        } else {
+            buffer.digest_pad(1, &bit_len.to_le_bytes(), |b| {
+                compress(&mut self.state, b.as_ref())
+            });
+        }
+
         for (chunk, v) in out.chunks_exact_mut(8).zip(self.state.iter()) {
             chunk.copy_from_slice(&v.to_le_bytes());
         }
     }
 }
 
-impl Default for TigerCore {
+impl<const VER2: bool> Default for TigerCore<VER2> {
+    #[inline]
     fn default() -> Self {
         Self {
             block_len: 0,
@@ -87,102 +101,44 @@ impl Default for TigerCore {
     }
 }
 
-impl Reset for TigerCore {
+impl<const VER2: bool> Reset for TigerCore<VER2> {
+    #[inline]
     fn reset(&mut self) {
         *self = Default::default();
     }
 }
 
-impl AlgorithmName for TigerCore {
+impl<const VER2: bool> AlgorithmName for TigerCore<VER2> {
+    #[inline]
     fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str("Tiger")
+        if VER2 {
+            f.write_str("Tiger2")
+        } else {
+            f.write_str("Tiger")
+        }
     }
 }
 
-impl fmt::Debug for TigerCore {
+impl<const VER2: bool> fmt::Debug for TigerCore<VER2> {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str("TigerCore { ... }")
-    }
-}
-
-/// Core Tiger2 hasher state.
-#[derive(Clone)]
-pub struct Tiger2Core {
-    block_len: u64,
-    state: State,
-}
-
-impl HashMarker for Tiger2Core {}
-
-impl BlockSizeUser for Tiger2Core {
-    type BlockSize = U64;
-}
-
-impl BufferKindUser for Tiger2Core {
-    type BufferKind = Eager;
-}
-
-impl OutputSizeUser for Tiger2Core {
-    type OutputSize = U24;
-}
-
-impl UpdateCore for Tiger2Core {
-    #[inline]
-    fn update_blocks(&mut self, blocks: &[Block<Self>]) {
-        self.block_len += blocks.len() as u64;
-        for block in blocks {
-            compress(&mut self.state, block.as_ref());
+        if VER2 {
+            f.write_str("Tiger2Core { ... }")
+        } else {
+            f.write_str("TigerCore { ... }")
         }
     }
 }
 
-impl FixedOutputCore for Tiger2Core {
+impl<const VER2: bool> Drop for TigerCore<VER2> {
     #[inline]
-    fn finalize_fixed_core(&mut self, buffer: &mut Buffer<Self>, out: &mut Output<Self>) {
-        let bs = Self::BlockSize::U64;
-        let pos = buffer.get_pos() as u64;
-        let bit_len = 8 * (pos + bs * self.block_len);
-
-        buffer.len64_padding_le(bit_len, |b| compress(&mut self.state, b.as_ref()));
-        for (chunk, v) in out.chunks_exact_mut(8).zip(self.state.iter()) {
-            chunk.copy_from_slice(&v.to_le_bytes());
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.state.zeroize();
+            self.block_len.zeroize();
         }
     }
 }
 
-impl Default for Tiger2Core {
-    fn default() -> Self {
-        Self {
-            block_len: 0,
-            state: [
-                0x0123_4567_89AB_CDEF,
-                0xFEDC_BA98_7654_3210,
-                0xF096_A5B4_C3B2_E187,
-            ],
-        }
-    }
-}
-
-impl Reset for Tiger2Core {
-    #[inline]
-    fn reset(&mut self) {
-        *self = Default::default();
-    }
-}
-
-impl AlgorithmName for Tiger2Core {
-    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str("Tiger2")
-    }
-}
-
-impl fmt::Debug for Tiger2Core {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        f.write_str("Tiger2Core { ... }")
-    }
-}
-
-/// Tiger hasher state.
-pub type Tiger = CoreWrapper<TigerCore>;
-/// Tiger2 hasher state.
-pub type Tiger2 = CoreWrapper<Tiger2Core>;
+#[cfg(feature = "zeroize")]
+impl<const VER2: bool> ZeroizeOnDrop for TigerCore<VER2> {}
diff --git a/tiger/src/tables.bin b/tiger/src/tables.bin
new file mode 100644
index 000000000..5911f7bcc
Binary files /dev/null and b/tiger/src/tables.bin differ
diff --git a/tiger/src/tables.rs b/tiger/src/tables.rs
index dc6e2c21e..5b73f299c 100644
--- a/tiger/src/tables.rs
+++ b/tiger/src/tables.rs
@@ -1,1035 +1,19 @@
-pub(crate) static T1: [u64; 256] = [
-    0x02AA_B17C_F7E9_0C5E,
-    0xAC42_4B03_E243_A8EC,
-    0x72CD_5BE3_0DD5_FCD3,
-    0x6D01_9B93_F6F9_7F3A,
-    0xCD99_78FF_D21F_9193,
-    0x7573_A1C9_7080_29E2,
-    0xB164_326B_922A_83C3,
-    0x4688_3EEE_0491_5870,
-    0xEAAC_E305_7103_ECE6,
-    0xC541_69B8_08A3_535C,
-    0x4CE7_5491_8DDE_C47C,
-    0x0AA2_F4DF_DC0D_F40C,
-    0x10B7_6F18_A74D_BEFA,
-    0xC6CC_B623_5AD1_AB6A,
-    0x1372_6121_572F_E2FF,
-    0x1A48_8C6F_199D_921E,
-    0x4BC9_F9F4_DA00_07CA,
-    0x26F5_E6F6_E852_41C7,
-    0x8590_79DB_EA59_47B6,
-    0x4F18_85C5_C99E_8C92,
-    0xD78E_761E_A96F_864B,
-    0x8E36_428C_52B5_C17D,
-    0x69CF_6827_3730_63C1,
-    0xB607_C93D_9BB4_C56E,
-    0x7D82_0E76_0E76_B5EA,
-    0x645C_9CC6_F07F_DC42,
-    0xBF38_A078_2433_42E0,
-    0x5F6B_343C_9D2E_7D04,
-    0xF2C2_8AEB_600B_0EC6,
-    0x6C0E_D85F_7254_BCAC,
-    0x7159_2281_A4DB_4FE5,
-    0x1967_FA69_CE0F_ED9F,
-    0xFD52_93F8_B965_45DB,
-    0xC879_E9D7_F2A7_600B,
-    0x8602_4892_0193_194E,
-    0xA4F9_533B_2D9C_C0B3,
-    0x9053_836C_1595_7613,
-    0xDB6D_CF8A_FC35_7BF1,
-    0x18BE_EA7A_7A37_0F57,
-    0x0371_17CA_50B9_9066,
-    0x6AB3_0A97_7442_4A35,
-    0xF4E9_2F02_E325_249B,
-    0x7739_DB07_061C_CAE1,
-    0xD8F3_B49C_ECA4_2A05,
-    0xBD56_BE3F_5138_2F73,
-    0x45FA_ED58_43B0_BB28,
-    0x1C81_3D5C_11BF_1F83,
-    0x8AF0_E4B6_D75F_A169,
-    0x33EE_18A4_87AD_9999,
-    0x3C26_E8EA_B1C9_4410,
-    0xB510_102B_C0A8_22F9,
-    0x141E_EF31_0CE6_123B,
-    0xFC65_B900_59DD_B154,
-    0xE015_8640_C5E0_E607,
-    0x884E_0798_26C3_A3CF,
-    0x930D_0D95_23C5_35FD,
-    0x3563_8D75_4E9A_2B00,
-    0x4085_FCCF_4046_9DD5,
-    0xC4B1_7AD2_8BE2_3A4C,
-    0xCAB2_F0FC_6A3E_6A2E,
-    0x2860_971A_6B94_3FCD,
-    0x3DDE_6EE2_12E3_0446,
-    0x6222_F32A_E017_65AE,
-    0x5D55_0BB5_4783_08FE,
-    0xA9EF_A98D_A0ED_A22A,
-    0xC351_A716_86C4_0DA7,
-    0x1105_586D_9C86_7C84,
-    0xDCFF_EE85_FDA2_2853,
-    0xCCFB_D026_2C5E_EF76,
-    0xBAF2_94CB_8990_D201,
-    0xE694_64F5_2AFA_D975,
-    0x94B0_13AF_DF13_3E14,
-    0x06A7_D1A3_2823_C958,
-    0x6F95_FE51_30F6_1119,
-    0xD92A_B34E_462C_06C0,
-    0xED7B_DE33_887C_71D2,
-    0x7974_6D6E_6518_393E,
-    0x5BA4_1938_5D71_3329,
-    0x7C1B_A6B9_48A9_7564,
-    0x3198_7C19_7BFD_AC67,
-    0xDE6C_23C4_4B05_3D02,
-    0x581C_49FE_D002_D64D,
-    0xDD47_4D63_3826_1571,
-    0xAA45_46C3_E473_D062,
-    0x928F_CE34_9455_F860,
-    0x4816_1BBA_CAAB_94D9,
-    0x6391_2430_770E_6F68,
-    0x6EC8_A5E6_02C6_641C,
-    0x8728_2515_337D_DD2B,
-    0x2CDA_6B42_034B_701B,
-    0xB03D_37C1_81CB_096D,
-    0xE108_4382_66C7_1C6F,
-    0x2B31_80C7_EB51_B255,
-    0xDF92_B82F_96C0_8BBC,
-    0x5C68_C8C0_A632_F3BA,
-    0x5504_CC86_1C3D_0556,
-    0xABBF_A4E5_5FB2_6B8F,
-    0x4184_8B0A_B3BA_CEB4,
-    0xB334_A273_AA44_5D32,
-    0xBCA6_96F0_A85A_D881,
-    0x24F6_EC65_B528_D56C,
-    0x0CE1_512E_90F4_524A,
-    0x4E9D_D79D_5506_D35A,
-    0x2589_05FA_C6CE_9779,
-    0x2019_295B_3E10_9B33,
-    0xF8A9_478B_73A0_54CC,
-    0x2924_F2F9_3441_7EB0,
-    0x3993_357D_536D_1BC4,
-    0x38A8_1AC2_1DB6_FF8B,
-    0x47C4_FBF1_7D60_16BF,
-    0x1E0F_AADD_7667_E3F5,
-    0x7ABC_FF62_938B_EB96,
-    0xA78D_AD94_8FC1_79C9,
-    0x8F1F_98B7_2911_E50D,
-    0x61E4_8EAE_2712_1A91,
-    0x4D62_F7AD_3185_9808,
-    0xECEB_A345_EF5C_EAEB,
-    0xF5CE_B25E_BC96_84CE,
-    0xF633_E20C_B7F7_6221,
-    0xA32C_DF06_AB82_93E4,
-    0x985A_202C_A5EE_2CA4,
-    0xCF0B_8447_CC8A_8FB1,
-    0x9F76_5244_9798_59A3,
-    0xA8D5_16B1_A124_0017,
-    0x0BD7_BA3E_BB5D_C726,
-    0xE54B_CA55_B86A_DB39,
-    0x1D7A_3AFD_6C47_8063,
-    0x519E_C608_E766_9EDD,
-    0x0E57_15A2_D149_AA23,
-    0x177D_4571_848F_F194,
-    0xEEB5_5F32_4101_4C22,
-    0x0F5E_5CA1_3A6E_2EC2,
-    0x8029_927B_75F5_C361,
-    0xAD13_9FAB_C3D6_E436,
-    0x0D5D_F1A9_4CCF_402F,
-    0x3E8B_D948_BEA5_DFC8,
-    0xA5A0_D357_BD3F_F77E,
-    0xA2D1_2E25_1F74_F645,
-    0x66FD_9E52_5E81_A082,
-    0x2E0C_90CE_7F68_7A49,
-    0xC2E8_BCBE_BA97_3BC5,
-    0x0000_01BC_E509_745F,
-    0x4237_77BB_E6DA_B3D6,
-    0xD166_1C7E_AEF0_6EB5,
-    0xA178_1F35_4DAA_CFD8,
-    0x2D11_284A_2B16_AFFC,
-    0xF1FC_4F67_FA89_1D1F,
-    0x73EC_C25D_CB92_0ADA,
-    0xAE61_0C22_C2A1_2651,
-    0x96E0_A810_D356_B78A,
-    0x5A9A_381F_2FE7_870F,
-    0xD5AD_62ED_E94E_5530,
-    0xD225_E5E8_368D_1427,
-    0x6597_7B70_C7AF_4631,
-    0x99F8_89B2_DE39_D74F,
-    0x233F_30BF_54E1_D143,
-    0x9A96_75D3_D9A6_3C97,
-    0x5470_554F_F334_F9A8,
-    0x166A_CB74_4A4F_5688,
-    0x70C7_4CAA_B2E4_AEAD,
-    0xF0D0_9164_6F29_4D12,
-    0x57B8_2A89_6840_31D1,
-    0xEFD9_5A5A_61BE_0B6B,
-    0x2FBD_12E9_69F2_F29A,
-    0x9BD3_7013_FEFF_9FE8,
-    0x3F9B_0404_D608_5A06,
-    0x4940_C1F3_166C_FE15,
-    0x0954_2C4D_CDF3_DEFB,
-    0xB4C5_2183_85CD_5CE3,
-    0xC935_B7DC_4462_A641,
-    0x3417_F8A6_8ED3_B63F,
-    0xB809_5929_5B21_5B40,
-    0xF99C_DAEF_3B8C_8572,
-    0x018C_0614_F8FC_B95D,
-    0x1B14_ACCD_1A3A_CDF3,
-    0x84D4_71F2_00BB_732D,
-    0xC1A3_110E_95E8_DA16,
-    0x430A_7220_BF1A_82B8,
-    0xB77E_090D_39DF_210E,
-    0x5EF4_BD9F_3CD0_5E9D,
-    0x9D4F_F6DA_7E57_A444,
-    0xDA1D_60E1_83D4_A5F8,
-    0xB287_C384_1799_8E47,
-    0xFE3E_DC12_1BB3_1886,
-    0xC7FE_3CCC_980C_CBEF,
-    0xE46F_B590_189B_FD03,
-    0x3732_FD46_9A4C_57DC,
-    0x7EF7_00A0_7CF1_AD65,
-    0x59C6_4468_A31D_8859,
-    0x762F_B0B4_D45B_61F6,
-    0x155B_AED0_9904_7718,
-    0x6875_5E4C_3D50_BAA6,
-    0xE921_4E7F_22D8_B4DF,
-    0x2ADD_BF53_2EAC_95F4,
-    0x32AE_3909_B4BD_0109,
-    0x834D_F537_B08E_3450,
-    0xFA20_9DA8_4220_728D,
-    0x9E69_1D9B_9EFE_23F7,
-    0x0446_D288_C4AE_8D7F,
-    0x7B4C_C524_E169_785B,
-    0x21D8_7F01_35CA_1385,
-    0xCEBB_400F_137B_8AA5,
-    0x272E_2B66_5807_96BE,
-    0x3612_2641_25C2_B0DE,
-    0x0577_02BD_AD1E_FBB2,
-    0xD4BA_BB8E_ACF8_4BE9,
-    0x9158_3139_641B_C67B,
-    0x8BDC_2DE0_8036_E024,
-    0x603C_8156_F49F_68ED,
-    0xF7D2_36F7_DBEF_5111,
-    0x9727_C459_8AD2_1E80,
-    0xA08A_0896_670A_5FD7,
-    0xCB4A_8F43_09EB_A9CB,
-    0x81AF_564B_0F70_36A1,
-    0xC0B9_9AA7_7819_9ABD,
-    0x959F_1EC8_3FC8_E952,
-    0x8C50_5077_794A_81B9,
-    0x3ACA_AF8F_0563_38F0,
-    0x07B4_3F50_627A_6778,
-    0x4A44_AB49_F5EC_CC77,
-    0x3BC3_D6E4_B679_EE98,
-    0x9CC0_D4D1_CF14_108C,
-    0x4406_C00B_206B_C8A0,
-    0x82A1_8854_C8D7_2D89,
-    0x67E3_66B3_5C3C_432C,
-    0xB923_DD61_102B_37F2,
-    0x56AB_2779_D884_271D,
-    0xBE83_E1B0_FF15_25AF,
-    0xFB7C_65D4_217E_49A9,
-    0x6BDB_E0E7_6D48_E7D4,
-    0x08DF_8287_45D9_179E,
-    0x22EA_6A9A_DD53_BD34,
-    0xE36E_141C_5622_200A,
-    0x7F80_5D1B_8CB7_50EE,
-    0xAFE5_C7A5_9F58_E837,
-    0xE27F_996A_4FB1_C23C,
-    0xD386_7DFB_0775_F0D0,
-    0xD0E6_73DE_6E88_891A,
-    0x123A_EB9E_AFB8_6C25,
-    0x30F1_D5D5_C145_B895,
-    0xBB43_4A2D_EE72_69E7,
-    0x78CB_67EC_F931_FA38,
-    0xF33B_0372_323B_BF9C,
-    0x52D6_6336_FB27_9C74,
-    0x505F_33AC_0AFB_4EAA,
-    0xE8A5_CD99_A2CC_E187,
-    0x5349_7480_1E2D_30BB,
-    0x8D2D_5711_D587_6D90,
-    0x1F1A_4128_91BC_038E,
-    0xD6E2_E71D_82E5_6648,
-    0x7403_6C3A_4977_32B7,
-    0x89B6_7ED9_6361_F5AB,
-    0xFFED_95D8_F1EA_02A2,
-    0xE72B_3BD6_1464_D43D,
-    0xA630_0F17_0BDC_4820,
-    0xEBC1_8760_ED78_A77A,
-];
+use core::mem::transmute_copy;
 
-pub(crate) static T2: [u64; 256] = [
-    0xE6A6_BE5A_05A1_2138,
-    0xB5A1_22A5_B4F8_7C98,
-    0x563C_6089_140B_6990,
-    0x4C46_CB2E_391F_5DD5,
-    0xD932_ADDB_C9B7_9434,
-    0x08EA_70E4_2015_AFF5,
-    0xD765_A667_3E47_8CF1,
-    0xC4FB_757E_AB27_8D99,
-    0xDF11_C686_2D6E_0692,
-    0xDDEB_84F1_0D7F_3B16,
-    0x6F2E_F604_A665_EA04,
-    0x4A8E_0F0F_F0E0_DFB3,
-    0xA5ED_EEF8_3DBC_BA51,
-    0xFC4F_0A2A_0EA4_371E,
-    0xE83E_1DA8_5CB3_8429,
-    0xDC8F_F882_BA1B_1CE2,
-    0xCD45_505E_8353_E80D,
-    0x18D1_9A00_D4DB_0717,
-    0x34A0_CFED_A5F3_8101,
-    0x0BE7_7E51_8887_CAF2,
-    0x1E34_1438_B3C4_5136,
-    0xE057_97F4_9089_CCF9,
-    0xFFD2_3F9D_F259_1D14,
-    0x543D_DA22_8595_C5CD,
-    0x661F_81FD_9905_2A33,
-    0x8736_E641_DB0F_7B76,
-    0x1522_7725_418E_5307,
-    0xE25F_7F46_162E_B2FA,
-    0x48A8_B212_6C13_D9FE,
-    0xAFDC_5417_92E7_6EEA,
-    0x03D9_12BF_C6D1_898F,
-    0x31B1_AAFA_1B83_F51B,
-    0xF1AC_2796_E42A_B7D9,
-    0x40A3_A7D7_FCD2_EBAC,
-    0x1056_136D_0AFB_BCC5,
-    0x7889_E1DD_9A6D_0C85,
-    0xD335_2578_2A79_74AA,
-    0xA7E2_5D09_078A_C09B,
-    0xBD41_38B3_EAC6_EDD0,
-    0x920A_BFBE_71EB_9E70,
-    0xA2A5_D0F5_4FC2_625C,
-    0xC054_E36B_0B12_90A3,
-    0xF6DD_59FF_62FE_932B,
-    0x3537_3545_11A8_AC7D,
-    0xCA84_5E91_72FA_DCD4,
-    0x84F8_2B60_329D_20DC,
-    0x79C6_2CE1_CD67_2F18,
-    0x8B09_A2AD_D124_642C,
-    0xD0C1_E96A_19D9_E726,
-    0x5A78_6A9B_4BA9_500C,
-    0x0E02_0336_634C_43F3,
-    0xC17B_474A_EB66_D822,
-    0x6A73_1AE3_EC9B_AAC2,
-    0x8226_667A_E084_0258,
-    0x67D4_5676_91CA_ECA5,
-    0x1D94_155C_4875_ADB5,
-    0x6D00_FD98_5B81_3FDF,
-    0x5128_6EFC_B774_CD06,
-    0x5E88_3447_1FA7_44AF,
-    0xF72C_A0AE_E761_AE2E,
-    0xBE40_E4CD_AEE8_E09A,
-    0xE997_0BBB_5118_F665,
-    0x726E_4BEB_33DF_1964,
-    0x703B_0007_2919_9762,
-    0x4631_D816_F5EF_30A7,
-    0xB880_B5B5_1504_A6BE,
-    0x6417_93C3_7ED8_4B6C,
-    0x7B21_ED77_F6E9_7D96,
-    0x7763_0631_2EF9_6B73,
-    0xAE52_8948_E86F_F3F4,
-    0x53DB_D7F2_86A3_F8F8,
-    0x16CA_DCE7_4CFC_1063,
-    0x005C_19BD_FA52_C6DD,
-    0x6886_8F5D_64D4_6AD3,
-    0x3A9D_512C_CF1E_186A,
-    0x367E_62C2_3856_60AE,
-    0xE359_E7EA_77DC_B1D7,
-    0x526C_0773_749A_BE6E,
-    0x735A_E5F9_D09F_734B,
-    0x493F_C7CC_8A55_8BA8,
-    0xB0B9_C153_3041_AB45,
-    0x3219_58BA_470A_59BD,
-    0x852D_B00B_5F46_C393,
-    0x9120_9B2B_D336_B0E5,
-    0x6E60_4F7D_659E_F19F,
-    0xB99A_8AE2_782C_CB24,
-    0xCCF5_2AB6_C814_C4C7,
-    0x4727_D9AF_BE11_727B,
-    0x7E95_0D0C_0121_B34D,
-    0x756F_4356_70AD_471F,
-    0xF5AD_D442_615A_6849,
-    0x4E87_E099_80B9_957A,
-    0x2ACF_A1DF_50AE_E355,
-    0xD898_263A_FD2F_D556,
-    0xC8F4_924D_D80C_8FD6,
-    0xCF99_CA3D_754A_173A,
-    0xFE47_7BAC_AF91_BF3C,
-    0xED53_71F6_D690_C12D,
-    0x831A_5C28_5E68_7094,
-    0xC5D3_C90A_3708_A0A4,
-    0x0F7F_9037_17D0_6580,
-    0x19F9_BB13_B8FD_F27F,
-    0xB1BD_6F1B_4D50_2843,
-    0x1C76_1BA3_8FFF_4012,
-    0x0D15_30C4_E2E2_1F3B,
-    0x8943_CE69_A737_2C8A,
-    0xE518_4E11_FEB5_CE66,
-    0x618B_DB80_BD73_6621,
-    0x7D29_BAD6_8B57_4D0B,
-    0x81BB_613E_25E6_FE5B,
-    0x071C_9C10_BC07_913F,
-    0xC7BE_EB79_09AC_2D97,
-    0xC3E5_8D35_3BC5_D757,
-    0xEB01_7892_F38F_61E8,
-    0xD4EF_FB9C_9B1C_C21A,
-    0x9972_7D26_F494_F7AB,
-    0xA3E0_63A2_956B_3E03,
-    0x9D4A_8B9A_4AA0_9C30,
-    0x3F6A_B7D5_0009_0FB4,
-    0x9CC0_F2A0_5726_8AC0,
-    0x3DEE_9D2D_EDBF_42D1,
-    0x330F_49C8_7960_A972,
-    0xC6B2_7202_8742_1B41,
-    0x0AC5_9EC0_7C00_369C,
-    0xEF4E_AC49_CB35_3425,
-    0xF450_244E_EF01_29D8,
-    0x8ACC_46E5_CAF4_DEB6,
-    0x2FFE_AB63_9892_63F7,
-    0x8F7C_B9FE_5D7A_4578,
-    0x5BD8_F764_4E63_4635,
-    0x427A_7315_BF2D_C900,
-    0x17D0_C4AA_2125_261C,
-    0x3992_486C_9351_8E50,
-    0xB4CB_FEE0_A2D7_D4C3,
-    0x7C75_D620_2C5D_DD8D,
-    0xDBC2_95D8_E35B_6C61,
-    0x60B3_69D3_0203_2B19,
-    0xCE42_685F_DCE4_4132,
-    0x06F3_DDB9_DDF6_5610,
-    0x8EA4_D21D_B5E1_48F0,
-    0x20B0_FCE6_2FCD_496F,
-    0x2C1B_9123_58B0_EE31,
-    0xB283_17B8_18F5_A308,
-    0xA89C_1E18_9CA6_D2CF,
-    0x0C6B_1857_6AAA_DBC8,
-    0xB65D_EAA9_1299_FAE3,
-    0xFB2B_794B_7F10_27E7,
-    0x04E4_317F_443B_5BEB,
-    0x4B85_2D32_5939_D0A6,
-    0xD5AE_6BEE_FB20_7FFC,
-    0x3096_82B2_81C7_D374,
-    0xBAE3_09A1_94C3_B475,
-    0x8CC3_F97B_13B4_9F05,
-    0x98A9_422F_F829_3967,
-    0x244B_16B0_1076_FF7C,
-    0xF8BF_571C_663D_67EE,
-    0x1F0D_6758_EEE3_0DA1,
-    0xC9B6_11D9_7ADE_B9B7,
-    0xB7AF_D588_7B6C_57A2,
-    0x6290_AE84_6B98_4FE1,
-    0x94DF_4CDE_ACC1_A5FD,
-    0x058A_5BD1_C548_3AFF,
-    0x6316_6CC1_42BA_3C37,
-    0x8DB8_526E_B2F7_6F40,
-    0xE108_8003_6F0D_6D4E,
-    0x9E05_23C9_971D_311D,
-    0x45EC_2824_CC7C_D691,
-    0x575B_8359_E623_82C9,
-    0xFA9E_400D_C488_9995,
-    0xD182_3ECB_4572_1568,
-    0xDAFD_983B_8206_082F,
-    0xAA7D_2908_2386_A8CB,
-    0x269F_CD44_03B8_7588,
-    0x1B91_F5F7_28BD_D1E0,
-    0xE466_9F39_0402_01F6,
-    0x7A1D_7C21_8CF0_4ADE,
-    0x6562_3C29_D79C_E5CE,
-    0x2368_4490_96C0_0BB1,
-    0xAB9B_F187_9DA5_03BA,
-    0xBC23_ECB1_A458_058E,
-    0x9A58_DF01_BB40_1ECC,
-    0xA070_E868_A85F_143D,
-    0x4FF1_8830_7DF2_239E,
-    0x14D5_65B4_1A64_1183,
-    0xEE13_3374_5270_1602,
-    0x950E_3DCF_3F28_5E09,
-    0x5993_0254_B9C8_0953,
-    0x3BF2_9940_8930_DA6D,
-    0xA955_943F_5369_1387,
-    0xA15E_DECA_A9CB_8784,
-    0x2914_2127_352B_E9A0,
-    0x76F0_371F_FF4E_7AFB,
-    0x0239_F450_274F_2228,
-    0xBB07_3AF0_1D5E_868B,
-    0xBFC8_0571_C10E_96C1,
-    0xD267_0885_6822_2E23,
-    0x9671_A3D4_8E80_B5B0,
-    0x55B5_D38A_E193_BB81,
-    0x693A_E2D0_A18B_04B8,
-    0x5C48_B4EC_ADD5_335F,
-    0xFD74_3B19_4916_A1CA,
-    0x2577_0181_34BE_98C4,
-    0xE779_87E8_3C54_A4AD,
-    0x28E1_1014_DA33_E1B9,
-    0x270C_C59E_226A_A213,
-    0x7149_5F75_6D1A_5F60,
-    0x9BE8_53FB_60AF_EF77,
-    0xADC7_86A7_F744_3DBF,
-    0x0904_4561_73B2_9A82,
-    0x58BC_7A66_C232_BD5E,
-    0xF306_558C_673A_C8B2,
-    0x41F6_39C6_B6C9_772A,
-    0x216D_EFE9_9FDA_35DA,
-    0x1164_0CC7_1C7B_E615,
-    0x93C4_3694_565C_5527,
-    0xEA03_8E62_4677_7839,
-    0xF9AB_F3CE_5A3E_2469,
-    0x741E_768D_0FD3_12D2,
-    0x0144_B883_CED6_52C6,
-    0xC20B_5A5B_A33F_8552,
-    0x1AE6_9633_C343_5A9D,
-    0x97A2_8CA4_088C_FDEC,
-    0x8824_A43C_1E96_F420,
-    0x3761_2FA6_6EEE_A746,
-    0x6B4C_B165_F9CF_0E5A,
-    0x43AA_1C06_A0AB_FB4A,
-    0x7F4D_C26F_F162_796B,
-    0x6CBA_CC8E_54ED_9B0F,
-    0xA6B7_FFEF_D2BB_253E,
-    0x2E25_BC95_B0A2_9D4F,
-    0x86D6_A58B_DEF1_388C,
-    0xDED7_4AC5_76B6_F054,
-    0x8030_BDBC_2B45_805D,
-    0x3C81_AF70_E94D_9289,
-    0x3EFF_6DDA_9E31_00DB,
-    0xB38D_C39F_DFCC_8847,
-    0x1238_8552_8D17_B87E,
-    0xF2DA_0ED2_40B1_B642,
-    0x44CE_FADC_D54B_F9A9,
-    0x1312_200E_433C_7EE6,
-    0x9FFC_C84F_3A78_C748,
-    0xF0CD_1F72_2485_76BB,
-    0xEC69_7405_3638_CFE4,
-    0x2BA7_B67C_0CEC_4E4C,
-    0xAC2F_4DF3_E5CE_32ED,
-    0xCB33_D143_26EA_4C11,
-    0xA4E9_044C_C77E_58BC,
-    0x5F51_3293_D934_FCEF,
-    0x5DC9_6455_06E5_5444,
-    0x50DE_418F_317D_E40A,
-    0x388C_B31A_69DD_E259,
-    0x2DB4_A834_5582_0A86,
-    0x9010_A91E_8471_1AE9,
-    0x4DF7_F0B7_B149_8371,
-    0xD62A_2EAB_C097_7179,
-    0x22FA_C097_AA8D_5C0E,
-];
+#[cfg(target_endian = "little")]
+pub(crate) static TABLES: [[u64; 256]; 4] = unsafe { transmute_copy(include_bytes!("tables.bin")) };
 
-pub(crate) static T3: [u64; 256] = [
-    0xF49F_CC2F_F1DA_F39B,
-    0x487F_D5C6_6FF2_9281,
-    0xE8A3_0667_FCDC_A83F,
-    0x2C9B_4BE3_D2FC_CE63,
-    0xDA3F_F74B_93FB_BBC2,
-    0x2FA1_65D2_FE70_BA66,
-    0xA103_E279_970E_93D4,
-    0xBECD_EC77_B0E4_5E71,
-    0xCFB4_1E72_3985_E497,
-    0xB70A_AA02_5EF7_5017,
-    0xD423_09F0_3840_B8E0,
-    0x8EFC_1AD0_3589_8579,
-    0x96C6_920B_E2B2_ABC5,
-    0x66AF_4163_375A_9172,
-    0x2174_ABDC_CA71_27FB,
-    0xB33C_CEA6_4A72_FF41,
-    0xF04A_4933_0830_66A5,
-    0x8D97_0ACD_D728_9AF5,
-    0x8F96_E8E0_31C8_C25E,
-    0xF3FE_C022_7687_5D47,
-    0xEC7B_F310_0561_90DD,
-    0xF5AD_B0AE_BB0F_1491,
-    0x9B50_F885_0FD5_8892,
-    0x4975_4883_58B7_4DE8,
-    0xA335_4FF6_9153_1C61,
-    0x0702_BBE4_81D2_C6EE,
-    0x89FB_2405_7DED_ED98,
-    0xAC30_7513_8596_E902,
-    0x1D2D_3580_1727_72ED,
-    0xEB73_8FC2_8E6B_C30D,
-    0x5854_EF8F_6304_4326,
-    0x9E5C_5232_5ADD_3BBE,
-    0x90AA_53CF_325C_4623,
-    0xC1D2_4D51_349D_D067,
-    0x2051_CFEE_A69E_A624,
-    0x1322_0F0A_862E_7E4F,
-    0xCE39_3994_04E0_4864,
-    0xD9C4_2CA4_7086_FCB7,
-    0x685A_D223_8A03_E7CC,
-    0x0664_84B2_AB2F_F1DB,
-    0xFE9D_5D70_EFBF_79EC,
-    0x5B13_B9DD_9C48_1854,
-    0x15F0_D475_ED15_09AD,
-    0x0BEB_CD06_0EC7_9851,
-    0xD58C_6791_183A_B7F8,
-    0xD118_7C50_52F3_EEE4,
-    0xC95D_1192_E54E_82FF,
-    0x86EE_A14C_B9AC_6CA2,
-    0x3485_BEB1_5367_7D5D,
-    0xDD19_1D78_1F8C_492A,
-    0xF608_66BA_A784_EBF9,
-    0x518F_643B_A2D0_8C74,
-    0x8852_E956_E108_7C22,
-    0xA768_CB8D_C410_AE8D,
-    0x3804_7726_BFEC_8E1A,
-    0xA677_38B4_CD3B_45AA,
-    0xAD16_691C_EC0D_DE19,
-    0xC6D4_3193_8046_2E07,
-    0xC5A5_876D_0BA6_1938,
-    0x16B9_FA1F_A58F_D840,
-    0x188A_B117_3CA7_4F18,
-    0xABDA_2F98_C99C_021F,
-    0x3E05_80AB_134A_E816,
-    0x5F3B_05B7_7364_5ABB,
-    0x2501_A2BE_5575_F2F6,
-    0x1B2F_7400_4E7E_8BA9,
-    0x1CD7_5803_71E8_D953,
-    0x7F6E_D895_6276_4E30,
-    0xB159_26FF_596F_003D,
-    0x9F65_293D_A8C5_D6B9,
-    0x6ECE_F04D_D690_F84C,
-    0x4782_275F_FF33_AF88,
-    0xE414_3308_3F82_0801,
-    0xFD0D_FE40_9A1A_F9B5,
-    0x4325_A334_2CDB_396B,
-    0x8AE7_7E62_B301_B252,
-    0xC36F_9E9F_6655_615A,
-    0x8545_5A2D_92D3_2C09,
-    0xF2C7_DEA9_4947_7485,
-    0x63CF_B4C1_33A3_9EBA,
-    0x83B0_40CC_6EBC_5462,
-    0x3B94_54C8_FDB3_26B0,
-    0x56F5_6A9E_87FF_D78C,
-    0x2DC2_940D_99F4_2BC6,
-    0x98F7_DF09_6B09_6E2D,
-    0x19A6_E01E_3AD8_52BF,
-    0x42A9_9CCB_DBD4_B40B,
-    0xA599_98AF_45E9_C559,
-    0x3662_95E8_07D9_3186,
-    0x6B48_181B_FAA1_F773,
-    0x1FEC_57E2_157A_0A1D,
-    0x4667_446A_F620_1AD5,
-    0xE615_EBCA_CFB0_F075,
-    0xB8F3_1F4F_6829_0778,
-    0x2271_3ED6_CE22_D11E,
-    0x3057_C1A7_2EC3_C93B,
-    0xCB46_ACC3_7C3F_1F2F,
-    0xDBB8_93FD_02AA_F50E,
-    0x331F_D92E_600B_9FCF,
-    0xA498_F961_48EA_3AD6,
-    0xA8D8_426E_8B6A_83EA,
-    0xA089_B274_B773_5CDC,
-    0x87F6_B373_1E52_4A11,
-    0x1188_08E5_CBC9_6749,
-    0x9906_E4C7_B19B_D394,
-    0xAFED_7F7E_9B24_A20C,
-    0x6509_EADE_EB36_44A7,
-    0x6C1E_F1D3_E8EF_0EDE,
-    0xB9C9_7D43_E979_8FB4,
-    0xA2F2_D784_740C_28A3,
-    0x7B84_9647_6197_566F,
-    0x7A5B_E3E6_B65F_069D,
-    0xF963_30ED_78BE_6F10,
-    0xEEE6_0DE7_7A07_6A15,
-    0x2B4B_EE4A_A08B_9BD0,
-    0x6A56_A63E_C7B8_894E,
-    0x0212_1359_BA34_FEF4,
-    0x4CBF_99F8_2837_03FC,
-    0x3980_7135_0CAF_30C8,
-    0xD0A7_7A89_F017_687A,
-    0xF1C1_A9EB_9E42_3569,
-    0x8C79_7628_2DEE_8199,
-    0x5D17_37A5_DD1F_7ABD,
-    0x4F53_433C_09A9_FA80,
-    0xFA8B_0C53_DF7C_A1D9,
-    0x3FD9_DCBC_886C_CB77,
-    0xC040_917C_A91B_4720,
-    0x7DD0_0142_F9D1_DCDF,
-    0x8476_FC1D_4F38_7B58,
-    0x23F8_E7C5_F331_6503,
-    0x032A_2244_E7E3_7339,
-    0x5C87_A5D7_50F5_A74B,
-    0x082B_4CC4_3698_992E,
-    0xDF91_7BEC_B858_F63C,
-    0x3270_B8FC_5BF8_6DDA,
-    0x10AE_72BB_29B5_DD76,
-    0x576A_C94E_7700_362B,
-    0x1AD1_12DA_C61E_FB8F,
-    0x691B_C30E_C5FA_A427,
-    0xFF24_6311_CC32_7143,
-    0x3142_368E_30E5_3206,
-    0x7138_0E31_E02C_A396,
-    0x958D_5C96_0AAD_76F1,
-    0xF8D6_F430_C16D_A536,
-    0xC8FF_D13F_1BE7_E1D2,
-    0x7578_AE66_004D_DBE1,
-    0x0583_3F01_067B_E646,
-    0xBB34_B5AD_3BFE_586D,
-    0x095F_34C9_A12B_97F0,
-    0x247A_B645_25D6_0CA8,
-    0xDCDB_C6F3_0174_77D1,
-    0x4A2E_14D4_DECA_D24D,
-    0xBDB5_E6D9_BE0A_1EEB,
-    0x2A7E_70F7_7943_01AB,
-    0xDEF4_2D8A_2705_40FD,
-    0x0107_8EC0_A34C_22C1,
-    0xE5DE_511A_F4C1_6387,
-    0x7EBB_3A52_BD9A_330A,
-    0x7769_7857_AA7D_6435,
-    0x004E_8316_03AE_4C32,
-    0xE7A2_1020_AD78_E312,
-    0x9D41_A70C_6AB4_20F2,
-    0x28E0_6C18_EA11_41E6,
-    0xD2B2_8CBD_984F_6B28,
-    0x26B7_5F6C_446E_9D83,
-    0xBA47_568C_4D41_8D7F,
-    0xD80B_ADBF_E618_3D8E,
-    0x0E20_6D7F_5F16_6044,
-    0xE258_A439_11CB_CA3E,
-    0x723A_1746_B21D_C0BC,
-    0xC7CA_A854_F5D7_CDD3,
-    0x7CAC_3288_3D26_1D9C,
-    0x7690_C264_23BA_942C,
-    0x17E5_5524_4780_42B8,
-    0xE0BE_4776_56A2_389F,
-    0x4D28_9B5E_67AB_2DA0,
-    0x4486_2B9C_8FBB_FD31,
-    0xB47C_C804_9D14_1365,
-    0x822C_1B36_2B91_C793,
-    0x4EB1_4655_FB13_DFD8,
-    0x1ECB_BA07_14E2_A97B,
-    0x6143_459D_5CDE_5F14,
-    0x53A8_FBF1_D5F0_AC89,
-    0x97EA_04D8_1C5E_5B00,
-    0x6221_81A8_D4FD_B3F3,
-    0xE9BC_D341_572A_1208,
-    0x1411_2586_43CC_E58A,
-    0x9144_C5FE_A4C6_E0A4,
-    0x0D33_D065_65CF_620F,
-    0x54A4_8D48_9F21_9CA1,
-    0xC43E_5EAC_6D63_C821,
-    0xA972_8B3A_7277_0DAF,
-    0xD793_4E7B_20DF_87EF,
-    0xE355_03B6_1A3E_86E5,
-    0xCAE3_21FB_C819_D504,
-    0x129A_50B3_AC60_BFA6,
-    0xCD5E_68EA_7E9F_B6C3,
-    0xB01C_9019_9483_B1C7,
-    0x3DE9_3CD5_C295_376C,
-    0xAED5_2EDF_2AB9_AD13,
-    0x2E60_F512_C0A0_7884,
-    0xBC3D_86A3_E362_10C9,
-    0x3526_9D9B_1639_51CE,
-    0x0C7D_6E2A_D0CD_B5FA,
-    0x59E8_6297_D87F_5733,
-    0x298E_F221_898D_B0E7,
-    0x5500_0029_D1A5_AA7E,
-    0x8BC0_8AE1_B506_1B45,
-    0xC2C3_1C2B_6C92_703A,
-    0x94CC_596B_AF25_EF42,
-    0x0A1D_73DB_2254_0456,
-    0x04B6_A0F9_D9C4_179A,
-    0xEFFD_AFA2_AE3D_3C60,
-    0xF7C8_075B_B494_96C4,
-    0x9CC5_C714_1D1C_D4E3,
-    0x78BD_1638_218E_5534,
-    0xB2F1_1568_F850_246A,
-    0xEDFA_BCFA_9502_BC29,
-    0x796C_E5F2_DA23_051B,
-    0xAAE1_28B0_DC93_537C,
-    0x3A49_3DA0_EE4B_29AE,
-    0xB5DF_6B2C_4168_95D7,
-    0xFCAB_BD25_122D_7F37,
-    0x7081_0B58_105D_C4B1,
-    0xE10F_DD37_F788_2A90,
-    0x524D_CAB5_518A_3F5C,
-    0x3C9E_8587_8451_255B,
-    0x4029_8281_19BD_34E2,
-    0x74A0_5B6F_5D3C_ECCB,
-    0xB610_0215_42E1_3ECA,
-    0x0FF9_79D1_2F59_E2AC,
-    0x6037_DA27_E4F9_CC50,
-    0x5E92_975A_0DF1_847D,
-    0xD66D_E190_D3E6_23FE,
-    0x5032_D6B8_7B56_8048,
-    0x9A36_B7CE_8235_216E,
-    0x8027_2A7A_24F6_4B4A,
-    0x93EF_ED8B_8C69_16F7,
-    0x37DD_BFF4_4CCE_1555,
-    0x4B95_DB5D_4B99_BD25,
-    0x92D3_FDA1_6981_2FC0,
-    0xFB1A_4A9A_9066_0BB6,
-    0x730C_1969_46A4_B9B2,
-    0x81E2_89AA_7F49_DA68,
-    0x6466_9A0F_83B1_A05F,
-    0x27B3_FF7D_9644_F48B,
-    0xCC6B_615C_8DB6_75B3,
-    0x674F_20B9_BCEB_BE95,
-    0x6F31_2382_7565_5982,
-    0x5AE4_8871_3E45_CF05,
-    0xBF61_9F99_54C2_1157,
-    0xEABA_C460_40A8_EAE9,
-    0x454C_6FE9_F2C0_C1CD,
-    0x419C_F649_6412_691C,
-    0xD3DC_3BEF_265B_0F70,
-    0x6D0E_60F5_C357_8A9E,
-];
-
-pub(crate) static T4: [u64; 256] = [
-    0x5B0E_6085_2632_3C55,
-    0x1A46_C1A9_FA1B_59F5,
-    0xA9E2_45A1_7C4C_8FFA,
-    0x65CA_5159_DB29_55D7,
-    0x05DB_0A76_CE35_AFC2,
-    0x81EA_C77E_A911_3D45,
-    0x528E_F88A_B6AC_0A0D,
-    0xA09E_A253_597B_E3FF,
-    0x430D_DFB3_AC48_CD56,
-    0xC4B3_A67A_F45C_E46F,
-    0x4ECE_CFD8_FBE2_D05E,
-    0x3EF5_6F10_B399_35F0,
-    0x0B22_D682_9CD6_19C6,
-    0x17FD_460A_74DF_2069,
-    0x6CF8_CC8E_8510_ED40,
-    0xD6C8_24BF_3A6E_CAA7,
-    0x6124_3D58_1A81_7049,
-    0x048B_ACB6_BBC1_63A2,
-    0xD9A3_8AC2_7D44_CC32,
-    0x7FDD_FF5B_AAF4_10AB,
-    0xAD6D_495A_A804_824B,
-    0xE1A6_A74F_2D8C_9F94,
-    0xD4F7_8512_35DE_E8E3,
-    0xFD4B_7F88_6540_D893,
-    0x247C_2004_2AA4_BFDA,
-    0x096E_A1C5_17D1_327C,
-    0xD569_66B4_361A_6685,
-    0x277D_A5C3_1221_057D,
-    0x94D5_9893_A43A_CFF7,
-    0x64F0_C51C_CDC0_2281,
-    0x3D33_BCC4_FF61_89DB,
-    0xE005_CB18_4CE6_6AF1,
-    0xFF5C_CD1D_1DB9_9BEA,
-    0xB0B8_54A7_FE42_980F,
-    0x7BD4_6A6A_718D_4B9F,
-    0xD10F_A8CC_22A5_FD8C,
-    0xD314_8495_2BE4_BD31,
-    0xC7FA_975F_CB24_3847,
-    0x4886_ED1E_5846_C407,
-    0x28CD_DB79_1EB7_0B04,
-    0xC2B0_0BE2_F573_417F,
-    0x5C95_9045_2180_F877,
-    0x7A6B_DDFF_F370_EB00,
-    0xCE50_9E38_D6D9_D6A4,
-    0xEBEB_0F00_647F_A702,
-    0x1DCC_06CF_7660_6F06,
-    0xE4D9_F28B_A286_FF0A,
-    0xD85A_305D_C918_C262,
-    0x475B_1D87_3222_5F54,
-    0x2D4F_B516_68CC_B5FE,
-    0xA679_B9D9_D72B_BA20,
-    0x5384_1C0D_912D_43A5,
-    0x3B7E_AA48_BF12_A4E8,
-    0x781E_0E47_F22F_1DDF,
-    0xEFF2_0CE6_0AB5_0973,
-    0x20D2_61D1_9DFF_B742,
-    0x16A1_2B03_062A_2E39,
-    0x1960_EB22_3965_0495,
-    0x251C_16FE_D50E_B8B8,
-    0x9AC0_C330_F826_016E,
-    0xED15_2665_953E_7671,
-    0x02D6_3194_A636_9570,
-    0x5074_F083_94B1_C987,
-    0x70BA_598C_90B2_5CE1,
-    0x794A_1581_0B97_42F6,
-    0x0D59_25E9_FCAF_8C6C,
-    0x3067_716C_D868_744E,
-    0x910A_B077_E8D7_731B,
-    0x6A61_BBDB_5AC4_2F61,
-    0x9351_3EFB_F085_1567,
-    0xF494_724B_9E83_E9D5,
-    0xE887_E198_5C09_648D,
-    0x34B1_D3C6_7537_0CFD,
-    0xDC35_E433_BC0D_255D,
-    0xD0AA_B842_3413_1BE0,
-    0x0804_2A50_B48B_7EAF,
-    0x9997_C4EE_44A3_AB35,
-    0x829A_7B49_2017_99D0,
-    0x263B_8307_B7C5_4441,
-    0x752F_95F4_FD6A_6CA6,
-    0x9272_1740_2C08_C6E5,
-    0x2A8A_B754_A795_D9EE,
-    0xA442_F755_2F72_943D,
-    0x2C31_334E_1978_1208,
-    0x4FA9_8D7C_EAEE_6291,
-    0x55C3_862F_665D_B309,
-    0xBD06_1017_5D53_B1F3,
-    0x46FE_6CB8_4041_3F27,
-    0x3FE0_3792_DF0C_FA59,
-    0xCFE7_0037_2EB8_5E8F,
-    0xA7BE_29E7_ADBC_E118,
-    0xE544_EE5C_DE84_31DD,
-    0x8A78_1B1B_41F1_873E,
-    0xA5C9_4C78_A0D2_F0E7,
-    0x3941_2E28_77B6_0728,
-    0xA126_5EF3_AFC9_A62C,
-    0xBCC2_770C_6A25_06C5,
-    0x3AB6_6DD5_DCE1_CE12,
-    0xE654_99D0_4A67_5B37,
-    0x7D8F_5234_81BF_D216,
-    0x0F6F_64FC_EC15_F389,
-    0x74EF_BE61_8B5B_13C8,
-    0xACDC_82B7_1427_3E1D,
-    0xDD40_BFE0_0319_9D17,
-    0x37E9_9257_E7E0_61F8,
-    0xFA52_6269_0477_5AAA,
-    0x8BBB_F63A_463D_56F9,
-    0xF001_3F15_43A2_6E64,
-    0xA830_7E9F_879E_C898,
-    0xCC4C_27A4_1501_77CC,
-    0x1B43_2F2C_CA1D_3348,
-    0xDE1D_1F8F_9F6F_A013,
-    0x6066_02A0_47A7_DDD6,
-    0xD237_AB64_CC1C_B2C7,
-    0x9B93_8E72_25FC_D1D3,
-    0xEC4E_0370_8E0F_F476,
-    0xFEB2_FBDA_3D03_C12D,
-    0xAE0B_CED2_EE43_889A,
-    0x22CB_8923_EBFB_4F43,
-    0x6936_0D01_3CF7_396D,
-    0x855E_3602_D2D4_E022,
-    0x0738_05BA_D01F_784C,
-    0x33E1_7A13_3852_F546,
-    0xDF48_7405_8AC7_B638,
-    0xBA92_B29C_678A_A14A,
-    0x0CE8_9FC7_6CFA_ADCD,
-    0x5F9D_4E09_0833_9E34,
-    0xF1AF_E929_1F59_23B9,
-    0x6E34_80F6_0F4A_265F,
-    0xEEBF_3A2A_B29B_841C,
-    0xE219_38A8_8F91_B4AD,
-    0x57DF_EFF8_45C6_D3C3,
-    0x2F00_6B0B_F62C_AAF2,
-    0x62F4_79EF_6F75_EE78,
-    0x11A5_5AD4_1C89_16A9,
-    0xF229_D290_84FE_D453,
-    0x42F1_C27B_16B0_00E6,
-    0x2B1F_7674_9823_C074,
-    0x4B76_ECA3_C274_5360,
-    0x8C98_F463_B916_91BD,
-    0x14BC_C93C_F1AD_E66A,
-    0x8885_213E_6D45_8397,
-    0x8E17_7DF0_274D_4711,
-    0xB49B_73B5_503F_2951,
-    0x1016_8168_C3F9_6B6B,
-    0x0E3D_963B_63CA_B0AE,
-    0x8DFC_4B56_55A1_DB14,
-    0xF789_F135_6E14_DE5C,
-    0x683E_68AF_4E51_DAC1,
-    0xC9A8_4F9D_8D4B_0FD9,
-    0x3691_E03F_52A0_F9D1,
-    0x5ED8_6E46_E187_8E80,
-    0x3C71_1A0E_99D0_7150,
-    0x5A08_65B2_0C4E_9310,
-    0x56FB_FC1F_E4F0_682E,
-    0xEA8D_5DE3_105E_DF9B,
-    0x71AB_FDB1_2379_187A,
-    0x2EB9_9DE1_BEE7_7B9C,
-    0x21EC_C0EA_33CF_4523,
-    0x59A4_D752_1805_C7A1,
-    0x3896_F5EB_56AE_7C72,
-    0xAA63_8F3D_B18F_75DC,
-    0x9F39_358D_ABE9_808E,
-    0xB7DE_FA91_C00B_72AC,
-    0x6B55_41FD_6249_2D92,
-    0x6DC6_DEE8_F92E_4D5B,
-    0x353F_57AB_C4BE_EA7E,
-    0x7357_69D6_DA56_90CE,
-    0x0A23_4AA6_4239_1484,
-    0xF6F9_5080_28F8_0D9D,
-    0xB8E3_19A2_7AB3_F215,
-    0x31AD_9C11_5134_1A4D,
-    0x773C_22A5_7BEF_5805,
-    0x45C7_561A_0796_8633,
-    0xF913_DA9E_249D_BE36,
-    0xDA65_2D9B_78A6_4C68,
-    0x4C27_A97F_3BC3_34EF,
-    0x7662_1220_E66B_17F4,
-    0x9677_4389_9ACD_7D0B,
-    0xF3EE_5BCA_E0ED_6782,
-    0x409F_7536_00C8_79FC,
-    0x06D0_9A39_B592_6DB6,
-    0x6F83_AEB0_317A_C588,
-    0x01E6_CA4A_8638_1F21,
-    0x66FF_3462_D19F_3025,
-    0x7220_7C24_DDFD_3BFB,
-    0x4AF6_B6D3_E2EC_E2EB,
-    0x9C99_4DBE_C7EA_08DE,
-    0x49AC_E597_B09A_8BC4,
-    0xB38C_4766_CF07_97BA,
-    0x131B_9373_C57C_2A75,
-    0xB182_2CCE_6193_1E58,
-    0x9D75_55B9_09BA_1C0C,
-    0x127F_AFDD_937D_11D2,
-    0x29DA_3BAD_C66D_92E4,
-    0xA2C1_D571_54C2_ECBC,
-    0x58C5_134D_82F6_FE24,
-    0x1C3A_E351_5B62_274F,
-    0xE907_C82E_01CB_8126,
-    0xF8ED_0919_13E3_7FCB,
-    0x3249_D8F9_C800_46C9,
-    0x80CF_9BED_E388_FB63,
-    0x1881_539A_116C_F19E,
-    0x5103_F3F7_6BD5_2457,
-    0x15B7_E6F5_AE47_F7A8,
-    0xDBD7_C6DE_D47E_9CCF,
-    0x44E5_5C41_0228_BB1A,
-    0xB647_D425_5EDB_4E99,
-    0x5D11_882B_B8AA_FC30,
-    0xF509_8BBB_29D3_212A,
-    0x8FB5_EA14_E902_96B3,
-    0x677B_9421_57DD_025A,
-    0xFB58_E7C0_A390_ACB5,
-    0x89D3_674C_83BD_4A01,
-    0x9E2D_A4DF_4BF3_B93B,
-    0xFCC4_1E32_8CAB_4829,
-    0x03F3_8C96_BA58_2C52,
-    0xCAD1_BDBD_7FD8_5DB2,
-    0xBBB4_42C1_6082_AE83,
-    0xB95F_E86B_A5DA_9AB0,
-    0xB22E_0467_3771_A93F,
-    0x8453_58C9_4931_52D8,
-    0xBE2A_4886_97B4_541E,
-    0x95A2_DC2D_D38E_6966,
-    0xC02C_11AC_923C_852B,
-    0x2388_B199_0DF2_A87B,
-    0x7C80_08FA_1B4F_37BE,
-    0x1F70_D0C8_4D54_E503,
-    0x5490_ADEC_7ECE_57D4,
-    0x002B_3C27_D906_3A3A,
-    0x7EAE_A384_8030_A2BF,
-    0xC602_326D_ED20_03C0,
-    0x83A7_287D_69A9_4086,
-    0xC57A_5FCB_30F5_7A8A,
-    0xB568_44E4_79EB_E779,
-    0xA373_B40F_05DC_BCE9,
-    0xD71A_786E_8857_0EE2,
-    0x879C_BACD_BDE8_F6A0,
-    0x976A_D1BC_C164_A32F,
-    0xAB21_E25E_9666_D78B,
-    0x9010_63AA_E5E5_C33C,
-    0x9818_B344_4869_8D90,
-    0xE364_87AE_3E1E_8ABB,
-    0xAFBD_F931_893B_DCB4,
-    0x6345_A0DC_5FBB_D519,
-    0x8628_FE26_9B94_65CA,
-    0x1E5D_0160_3F9C_51EC,
-    0x4DE4_4006_A150_49B7,
-    0xBF6C_70E5_F776_CBB1,
-    0x4112_18F2_EF55_2BED,
-    0xCB0C_0708_705A_36A3,
-    0xE74D_1475_4F98_6044,
-    0xCD56_D943_0EA8_280E,
-    0xC125_91D7_535F_5065,
-    0xC832_23F1_720A_EF96,
-    0xC3A0_396F_7363_A51F,
-];
+#[cfg(target_endian = "big")]
+pub(crate) static TABLES: [[u64; 256]; 4] = {
+    let mut tables: [[u64; 256]; 4] = unsafe { transmute_copy(include_bytes!("tables.bin")) };
+    let mut i = 0;
+    while i < 4 {
+        let mut j = 0;
+        while j < 256 {
+            tables[i][j] = tables[i][j].swap_bytes();
+            j += 1;
+        }
+        i += 1;
+    }
+    tables
+};
diff --git a/whirlpool/Cargo.toml b/whirlpool/Cargo.toml
index 71e7f64e0..b8ed88f2f 100644
--- a/whirlpool/Cargo.toml
+++ b/whirlpool/Cargo.toml
@@ -22,3 +22,8 @@ hex-literal = "0.4"
 [features]
 default = ["std"]
 std = ["digest/std"]
+zeroize = ["digest/zeroize"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/whirlpool/src/lib.rs b/whirlpool/src/lib.rs
index 5e249bec8..7972b2a7d 100644
--- a/whirlpool/src/lib.rs
+++ b/whirlpool/src/lib.rs
@@ -4,6 +4,7 @@
     html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
     html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
 )]
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![deny(unsafe_code)]
 #![warn(missing_docs, rust_2018_idioms)]
 
@@ -21,6 +22,9 @@ use digest::{
     HashMarker, Output,
 };
 
+#[cfg(feature = "zeroize")]
+use digest::zeroize::{Zeroize, ZeroizeOnDrop};
+
 mod compress;
 mod consts;
 
@@ -33,6 +37,9 @@ pub struct WhirlpoolCore {
     state: [u64; 8],
 }
 
+/// Whirlpool hasher state.
+pub type Whirlpool = CoreWrapper<WhirlpoolCore>;
+
 impl HashMarker for WhirlpoolCore {}
 
 impl BlockSizeUser for WhirlpoolCore {
@@ -81,6 +88,13 @@ impl FixedOutputCore for WhirlpoolCore {
 
 impl WhirlpoolCore {
     fn update_len(&mut self, len: u64) {
+        #[inline(always)]
+        fn adc(a: &mut u64, b: u64, carry: &mut u64) {
+            let ret = (*a as u128) + (b as u128) + (*carry as u128);
+            *a = ret as u64;
+            *carry = (ret >> 64) as u64;
+        }
+
         let mut carry = 0;
         adc(&mut self.bit_len[3], len, &mut carry);
         adc(&mut self.bit_len[2], 0, &mut carry);
@@ -120,12 +134,15 @@ impl fmt::Debug for WhirlpoolCore {
     }
 }
 
-/// Whirlpool hasher state.
-pub type Whirlpool = CoreWrapper<WhirlpoolCore>;
-
-#[inline(always)]
-fn adc(a: &mut u64, b: u64, carry: &mut u64) {
-    let ret = (*a as u128) + (b as u128) + (*carry as u128);
-    *a = ret as u64;
-    *carry = (ret >> 64) as u64;
+impl Drop for WhirlpoolCore {
+    fn drop(&mut self) {
+        #[cfg(feature = "zeroize")]
+        {
+            self.state.zeroize();
+            self.bit_len.zeroize();
+        }
+    }
 }
+
+#[cfg(feature = "zeroize")]
+impl ZeroizeOnDrop for WhirlpoolCore {}


Test patch:
diff --git a/jh/tests/lib.rs b/jh/tests/mod.rs
similarity index 100%
rename from jh/tests/lib.rs
rename to jh/tests/mod.rs