Skip to content

Commit

Permalink
feat: support zeroizing secrets
Browse files Browse the repository at this point in the history
  • Loading branch information
eopb committed Mar 24, 2024
1 parent 9aa340c commit d8c5e17
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 4 deletions.
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,20 @@ default = ["std"]
std = []
fake = ["dep:fake", "dep:rand"]
serde = ["dep:serde"]
zeroize = ["dep:zeroize"]

[dependencies]
fake = { version = "2.5", optional = true, default-features = false }
rand = { version = "0.8", optional = true, default-features = false }
serde = { version = "1.0", optional = true, default-features = false }
zeroize = { version = "1.7", optional = true, default-features = false }

[dev-dependencies]
redact = { features = ["serde", "fake"], path = "." }
redact = { features = ["serde", "fake", "zeroize"], path = "." }
fake = { features = ["derive"] }
serde = { features = ["derive"] }
serde_json = "1.0"
zeroize = { features = ["std"] }

[package.metadata.docs.rs]
all-features = true
Expand Down
51 changes: 48 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,23 +68,68 @@ struct Payment {

If you would like to implement `Serialize` without exposing the `Secret` see [serde::redact_secret].

## Zeroizing `Secret`s

`redact` does not require `Secret`s to be [`Zeroize`][::zeroize::Zeroize]able but does allow `Secret`s to be `Zeroize`d when the contained secret is `Zeroize`able.
To be able to `Zeroize` `Secret`s, enable `zeroize` in your `Cargo.toml`.

```toml
redact = { version = "0.1", features = ["zeroize"] }
zeroize = "1"
```

Once enabled, it is possible zeroize secrets.

```rust
# use redact::Secret;
use zeroize::Zeroize;


fn main() {
let mut secret = Secret::new("hunter2".to_owned());

// [ ... ] use secret here

// Now that we're done using the secret, zero it out.
secret.zeroize();
# assert_ne!(*secret.expose_secret(), "hunter2")
}
```

If you would like your `Secret` to be automatically `Zeroize`d when it is no longer being used,
consider wrapping your `Secret` in [`Zeroizing`][::zeroize::Zeroizing] which will `Zeroize` your secret when it is [`Drop`]ed

```rust
# use redact::Secret;
use zeroize::Zeroizing;


fn main() {
let mut secret = Zeroizing::new(Secret::new("hunter2".to_owned()));

// [ ... ] use secret here

// The secret is automatically zeroed out at the end of the scope when it is dropped
}
```

## Comparison with alternatives

### [secrecy](https://docs.rs/secrecy/latest/secrecy/)

[Secrecy](https://crates.io/crates/secrecy) was the original inspiration for this crate and it has a similar API.

One significant difference is that secrecy requires that all secrets implement [`Zeroize`] so that it can cleanly wipe secrets from memory after they are dropped.
One significant difference is that secrecy requires that all secrets implement [`Zeroize`](https://docs.rs/secrecy/latest/secrecy/trait.Zeroize.html) so that it can cleanly wipe secrets from memory after they are dropped.
This unfortunately limits the types of values that secrecy can wrap in a `Secret` since every type has to be aware of `Zeroize`.

Redact relaxes this requirement, allowing all types to be `Secret`s. If you need zeroization consider [secrecy](https://crates.io/crates/secrecy).
Redact relaxes this requirement, allowing all types to be `Secret`s.
When zeroizing is required, consider the techniques [above](#zeroizing-secrets).

### [secrets](https://docs.rs/secrets/latest/secrets/)

[Secrets](https://crates.io/crates/secrets) provides even stronger memory protection than [secrecy](#secrecy) using [`mlock(2)`]/[`mprotect(2)`] among other things.
If you need strong memory protection before and after a `Secret` is dropped consider [secrets](https://crates.io/crates/secrets).

[`Zeroize`]: https://docs.rs/secrecy/latest/secrecy/trait.Zeroize.html
[`mlock(2)`]: https://man7.org/linux/man-pages/man2/mlock.2.html
[`mprotect(2)`]: https://man7.org/linux/man-pages/man2/mprotect.2.html

Expand Down
2 changes: 2 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ mod fake;
mod ops;
#[cfg(feature = "serde")]
pub mod serde;
#[cfg(feature = "zeroize")]
mod zeroize;

#[cfg(feature = "serde")]
pub use crate::serde::expose_secret;
Expand Down
23 changes: 23 additions & 0 deletions src/zeroize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use crate::Secret;

use zeroize::{TryZeroize, Zeroize, ZeroizeOnDrop};

impl<T: Zeroize + ?Sized> Zeroize for Secret<T> {
fn zeroize(&mut self) {
self.0.zeroize()
}
}

impl<T: TryZeroize> TryZeroize for Secret<T> {
fn try_zeroize(&mut self) -> bool {
self.0.try_zeroize()
}
}

impl<T: ZeroizeOnDrop> ZeroizeOnDrop for Secret<T> {}

impl<T: Zeroize> Secret<T> {
pub fn zeroizing(self) -> zeroize::Zeroizing<Self> {
zeroize::Zeroizing::new(self)
}
}

0 comments on commit d8c5e17

Please sign in to comment.