#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
extern crate alloc;
use alloc::{boxed::Box, string::String, vec::Vec};
use core::{
any,
fmt::{self, Debug},
};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(feature = "serde")]
use serde::{de, ser, Deserialize, Serialize};
pub use zeroize;
pub struct SecretBox<S: Zeroize + ?Sized> {
inner_secret: Box<S>,
}
impl<S: Zeroize + ?Sized> Zeroize for SecretBox<S> {
fn zeroize(&mut self) {
self.inner_secret.as_mut().zeroize()
}
}
impl<S: Zeroize + ?Sized> Drop for SecretBox<S> {
fn drop(&mut self) {
self.zeroize()
}
}
impl<S: Zeroize + ?Sized> ZeroizeOnDrop for SecretBox<S> {}
impl<S: Zeroize + ?Sized> From<Box<S>> for SecretBox<S> {
fn from(source: Box<S>) -> Self {
Self::new(source)
}
}
impl<S: Zeroize + ?Sized> SecretBox<S> {
pub fn new(boxed_secret: Box<S>) -> Self {
Self {
inner_secret: boxed_secret,
}
}
}
impl<S: Zeroize + Default> SecretBox<S> {
pub fn init_with_mut(ctr: impl FnOnce(&mut S)) -> Self {
let mut secret = Self::default();
ctr(secret.expose_secret_mut());
secret
}
}
impl<S: Zeroize + Clone> SecretBox<S> {
pub fn init_with(ctr: impl FnOnce() -> S) -> Self {
let mut data = ctr();
let secret = Self {
inner_secret: Box::new(data.clone()),
};
data.zeroize();
secret
}
pub fn try_init_with<E>(ctr: impl FnOnce() -> Result<S, E>) -> Result<Self, E> {
let mut data = ctr()?;
let secret = Self {
inner_secret: Box::new(data.clone()),
};
data.zeroize();
Ok(secret)
}
}
impl<S: Zeroize + Default> Default for SecretBox<S> {
fn default() -> Self {
Self {
inner_secret: Box::<S>::default(),
}
}
}
impl<S: Zeroize + ?Sized> Debug for SecretBox<S> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "SecretBox<{}>([REDACTED])", any::type_name::<S>())
}
}
impl<S> Clone for SecretBox<S>
where
S: CloneableSecret,
{
fn clone(&self) -> Self {
SecretBox {
inner_secret: self.inner_secret.clone(),
}
}
}
impl<S: Zeroize + ?Sized> ExposeSecret<S> for SecretBox<S> {
fn expose_secret(&self) -> &S {
self.inner_secret.as_ref()
}
}
impl<S: Zeroize + ?Sized> ExposeSecretMut<S> for SecretBox<S> {
fn expose_secret_mut(&mut self) -> &mut S {
self.inner_secret.as_mut()
}
}
pub type SecretSlice<S> = SecretBox<[S]>;
impl<S> From<Vec<S>> for SecretSlice<S>
where
S: Zeroize,
[S]: Zeroize,
{
fn from(vec: Vec<S>) -> Self {
Self::from(vec.into_boxed_slice())
}
}
impl<S> Clone for SecretSlice<S>
where
S: CloneableSecret + Zeroize,
[S]: Zeroize,
{
fn clone(&self) -> Self {
SecretBox {
inner_secret: Vec::from(&*self.inner_secret).into_boxed_slice(),
}
}
}
impl<S> Default for SecretSlice<S>
where
S: Zeroize,
[S]: Zeroize,
{
fn default() -> Self {
Vec::new().into()
}
}
pub type SecretString = SecretBox<str>;
impl From<String> for SecretString {
fn from(s: String) -> Self {
Self::from(s.into_boxed_str())
}
}
impl From<&str> for SecretString {
fn from(s: &str) -> Self {
Self::from(String::from(s))
}
}
impl Clone for SecretString {
fn clone(&self) -> Self {
SecretBox {
inner_secret: self.inner_secret.clone(),
}
}
}
impl Default for SecretString {
fn default() -> Self {
String::default().into()
}
}
pub trait CloneableSecret: Clone + Zeroize {}
impl CloneableSecret for i8 {}
impl CloneableSecret for i16 {}
impl CloneableSecret for i32 {}
impl CloneableSecret for i64 {}
impl CloneableSecret for i128 {}
impl CloneableSecret for isize {}
impl CloneableSecret for u8 {}
impl CloneableSecret for u16 {}
impl CloneableSecret for u32 {}
impl CloneableSecret for u64 {}
impl CloneableSecret for u128 {}
impl CloneableSecret for usize {}
pub trait ExposeSecret<S: ?Sized> {
fn expose_secret(&self) -> &S;
}
pub trait ExposeSecretMut<S: ?Sized> {
fn expose_secret_mut(&mut self) -> &mut S;
}
#[cfg(feature = "serde")]
pub trait SerializableSecret: Serialize {}
#[cfg(feature = "serde")]
impl<'de, T> Deserialize<'de> for SecretBox<T>
where
T: Zeroize + Clone + de::DeserializeOwned + Sized,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
Self::try_init_with(|| T::deserialize(deserializer))
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for SecretString {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
String::deserialize(deserializer).map(Into::into)
}
}
#[cfg(feature = "serde")]
impl<T> Serialize for SecretBox<T>
where
T: Zeroize + SerializableSecret + Serialize + Sized,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
self.expose_secret().serialize(serializer)
}
}