8 releases
0.2.6 | Jul 18, 2024 |
---|---|
0.2.5 | Jul 17, 2024 |
0.2.4 | Apr 25, 2024 |
0.2.0 | Mar 16, 2024 |
0.1.0 | Jan 27, 2024 |
#310 in Rust patterns
65KB
722 lines
Secrets Management crate with
- type level and compile-time guarantees and
- each reference corresponds to each secret that can only be exposed or revealed under a lexical scope with an invariant lifetime
It is similar to the secrecy
crate but with type level and compile-time guarantees that the Secret<T, MEC, EC>
value is not ’exposed’ more than MEC
number of times and is only exposed under a well-defined lexical scope.
It makes use of the typenum
crate for all its compile-time guarantees.
Features
- Exposure Control: Secret values can only be exposed a limited number of times, preventing unintentional information leaks. This is guaranteed at compile time. Secrets are exposed and available for use with an invariant lifetime, identifiable with a clear lexical scope.
- Zeroization: If configured with the "zeroize" feature, secrets are zeroized upon dropping them.
- Cloneable Secrets: With the "cloneable-secret" feature,
Secret
values can be cloned if the underlying type,T
, implements theCloneableSecret
trait. - Debugging Secrets: The "debug-secret" feature enables the debugging of
Secret
values if the underlying type,T
, implements theDebugSecret
trait.
Usage Examples
Compile Time Checks
use sosecrets_rs::{
prelude::*,
traits::ExposeSecret,
};
use typenum::U2;
// Define a secret with a maximum exposure count of 2
let secret = Secret::<_, U2>::new("my_secret_value".to_string());
// Expose the secret and perform some operations with the exposed value; secret has been exposed once: `EC` = 1, `MEC` = 2;
let (next_secret, exposed_value) = secret.expose_secret(|exposed_secret| {
// `exposed_secret` is only 'available' from the next line -------
assert_eq!(&*exposed_secret.as_str(), "my_secret_value"); // ^
// Perform operations with the exposed value |
// ... v
// to this line... -----------------------------------------------
});
// Expose the secret again and perform some operations with the exposed value; secret has been exposed twice: `EC` = 2, `MEC` = 2;
let (next_secret, exposed_value) = next_secret.expose_secret(|exposed_secret| {
assert_eq!(&*exposed_secret.as_str(), "my_secret_value");
// Perform operations with the exposed value
// ...
});
Try to expose the secret again and perform some operations with the exposed value; secret has been exposed the third time: EC
= 3, MEC
= 2;
The following is uncompilable.
let (next_secret, exposed_value) = next_secret.expose_secret(|exposed_secret| {
assert_eq!(&*exposed_secret.as_str(), "my_secret_value");
// Perform operations with the exposed value
// ...
});
It is impossible to return the value (e.g. exposed_secret
in the example above) passed into the closure, out of the closure.
The following is uncompilable.
let (next_secret, exposed_value) = next_secret.expose_secret(|exposed_secret| {
assert_eq!(&*exposed_secret.as_str(), "my_secret_value");
// Perform operations with the exposed value
// ...
exposed_secret // impossible to return `exposed_secret` here
});
Note: If T
is Copy
, then the above will compile successfully and expose_secret(...)
method will return a copy of exposed T
.
Runtime Checks
use sosecrets_rs::{
prelude::*,
// Note, for runtime checks, you have to use the `RTExposeSecret` trait instead.
runtime::traits::RTExposeSecret,
};
use typenum::U2;
// Define a secret with a maximum exposure count of 2
let secret = RTSecret::<_, U2>::new("my_secret_value".to_string());
// Expose the secret and perform some operations with the exposed value; secret has been exposed once: `EC` = 1, `MEC` = 2;
let exposed_value = secret.expose_secret(|exposed_secret| {
// `exposed_secret` is only 'available' from the next line -------
assert_eq!(&*exposed_secret.as_str(), "my_secret_value"); // ^
// Perform operations with the exposed value |
// ... v
// to this line... -----------------------------------------------
});
// Expose the secret again and perform some operations with the exposed value; secret has been exposed twice: `EC` = 2, `MEC` = 2;
let exposed_value = secret.expose_secret(|exposed_secret| {
assert_eq!(&*exposed_secret.as_str(), "my_secret_value");
// Perform operations with the exposed value
// ...
});
Try to expose the secret again and perform some operations with the exposed value; secret has been exposed the third time: EC
= 3, MEC
= 2;
.expose_secret(...)
method will then panic
with the message:
`RTSecret\` has already been exposed for 2 times, the maximum number it is allowed to be exposed for is 2 times."
# use sosecrets_rs::{
# prelude::*,
# // Note, for runtime checks, you have to use the `RTExposeSecret` trait instead.
# runtime::traits::RTExposeSecret,
# };
# use typenum::U2;
#
# // Define a secret with a maximum exposure count of 2
# let secret = RTSecret::<_, U2>::new("my_secret_value".to_string());
#
# // Expose the secret and perform some operations with the exposed value; secret has been exposed once: `EC` = 1, `MEC` = 2;
# let exposed_value = secret.expose_secret(|exposed_secret| {
# // `exposed_secret` is only 'available' from the next line -------
# assert_eq!(&*exposed_secret.as_str(), "my_secret_value"); // ^
# // Perform operations with the exposed value |
# // ... v
# // to this line... -----------------------------------------------
# });
#
# // Expose the secret again and perform some operations with the exposed value; secret has been exposed twice: `EC` = 2, `MEC` = 2;
# let exposed_value = secret.expose_secret(|exposed_secret| {
# assert_eq!(&*exposed_secret.as_str(), "my_secret_value");
# // Perform operations with the exposed value
# // ...
# });
let exposed_value = secret.expose_secret(|exposed_secret| {
assert_eq!(&*exposed_secret.as_str(), "my_secret_value");
// Perform operations with the exposed value
// ...
});
Note: You can use the non-panicking variant of the method expose_secret(...)
which is named as try_expose_secret(...)
.
try_expose_secret(...)
returns a Result::Err
if the exposure count is larger than what is maximally allowed.
It is impossible to return the value (e.g. exposed_secret
in the example above) passed into the closure, out of the closure, unless T
is Copy
. The following is uncompilable.
let exposed_value = secret.expose_secret(|exposed_secret| {
assert_eq!(&*exposed_secret.as_str(), "my_secret_value");
// Perform operations with the exposed value
// ...
exposed_secret // impossible to return `exposed_secret` here
});
Substitute for the secrecy
crate
You can use the SecrecySecret
type as a substitute for the Secret<T>
in secrecy
crate.
use sosecrets_rs::{
prelude::*,
// Note, for runtime checks, you have to use the `RTExposeSecret` trait instead.
runtime::traits::RTExposeSecret,
};
// Define a secret with NO maximum exposure count
let secret = SecrecySecret::new("my_secret_value".to_string());
// Expose the secret and perform some operations with the exposed value as many times as you like.
for _ in 0..=1_000_000 {
let exposed_value = secret.expose_secret(|exposed_secret| {
// `exposed_secret` is only 'available' from the next line -------
assert_eq!(&*exposed_secret.as_str(), "my_secret_value"); // ^
// Perform operations with the exposed value |
// ... v
// to this line... -----------------------------------------------
});
}
See more in the examples directory.
Features Configuration
To enable features, you can include them in your Cargo.toml
:
[dependencies]
sosecrets-rs = { version = "x.x.x", features = ["zeroize", "cloneable-secret", "debug-secret"] }
Modules
prelude
: Module for easily importing common items.runtime
: Module forRTSecret<T>
,SecrecySecret
andRTExposeSecret
.
Traits
ExposeSecret
: Trait for safely exposing secrets with a limited exposure count at compile time.RTExposeSecret
: Trait for safely exposing secrets with a limited exposure count at runtime time.CloneableSecret
: Trait for cloneable secrets.DebugSecret
: Trait for debuggable secrets.
For example, if the feature "cloneable-secret"
is enabled, then you can 'clone' the secret.
Example:
#[cfg(all(feature = "cloneable-secret", feature = "alloc"))]
// Need to enable feature = "alloc" because `String` requires feature = "alloc".
{
use sosecrets_rs::{
prelude::*,
traits::{CloneableSecret, ExposeSecret},
};
use typenum::U2;
// Define a secret with a maximum exposure count of 2
let secret = Secret::<_, U2>::new("my_secret_value".to_string());
// Clone the secret
let secret2 = secret.clone();
// Expose the secret and perform some operations with the exposed value; secret has been exposed once: `EC` = 1, `MEC` = 2;
let (next_secret, exposed_value) = secret.expose_secret(move |exposed_secret| {
// `exposed_secret` is only 'available' from the next line --------------------------^
let (next_secret2, exposed_value2) = secret2.expose_secret(|exposed_secret2| { // |
assert_eq!(&*exposed_secret.as_str(), "my_secret_value"); // |
assert_eq!(&*exposed_secret2.as_str(), "my_secret_value"); // |
assert_eq!(&*exposed_secret2.as_str(), &*exposed_secret.as_str()); // |
// Perform operations with the exposed value |
// ... |
// to this line... ---------------------------------------------------------------v
});
});
}
Minimum Supported Rust version
The crate currently requires Rust 1.70. I have no intent on increasing the compiler version requirement of this crate beyond this. However, this is only guaranteed within a given minor version number.
Tests
Run
bash scripts/tests-all-features.sh
License
Licensed under
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the MIT license, without any additional terms or conditions.
Credits
CAD97
- For rendering substantial help in the design and implementations of
ExposeSecret
[Rust Forum, Rust Playground] trait and its trait method,expose_secret(...)
[Rust Forum, Rust Playground]. - For teaching me the concept of
invariant
lifetime.
Eric Michael Sumner
- For creating the macro
impl_choose_int!()
on Rust Forum. The macro helps to implement the traitChooseMinimallyRepresentableUInt
for all type-level unsigned integers provided by thetypenum
crate that are representable from 1 bit to 64 bits at the type level.
Simon Farnsworth
Dependencies
~165KB