use crate::string::{Bytes, SecretBuf};
pub trait LoadSecret {
fn size_hint(&self) -> usize {
32
}
fn load_secret(self, data: &mut SecretBuf) -> std::io::Result<()>;
}
impl LoadSecret for () {
fn size_hint(&self) -> usize {
0
}
fn load_secret(self, _: &mut SecretBuf) -> std::io::Result<()> {
Err(std::io::Error::new(std::io::ErrorKind::Unsupported, "no secret loader"))
}
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct Secret<T, L>(T, std::marker::PhantomData<L>);
impl<T: Clone, L> Clone for Secret<T, L> {
fn clone(&self) -> Self {
Secret(self.0.clone(), std::marker::PhantomData)
}
}
impl<T: Copy, L> Copy for Secret<T, L> {}
impl<T, L> Secret<T, L> {
pub fn new(value: T) -> Self {
Secret(value, std::marker::PhantomData)
}
pub fn into_inner(this: Self) -> T {
this.0
}
}
impl<'a, T, L: LoadSecret> Secret<T, L>
where
T: TryFrom<Bytes<'a>>,
T::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
{
pub fn load(value: L) -> Result<Self, std::io::Error> {
let mut buf = SecretBuf::with_capacity(value.size_hint());
value.load_secret(&mut buf)?;
let loaded = buf
.into_bytes()
.try_into()
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
Ok(Secret::new(loaded))
}
}
impl<T, L> std::ops::Deref for Secret<T, L> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T, L> std::ops::DerefMut for Secret<T, L> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Clone)]
pub struct Clear(pub SecretBuf);
impl LoadSecret for Clear {
fn size_hint(&self) -> usize {
0 }
fn load_secret(self, data: &mut SecretBuf) -> std::io::Result<()> {
std::mem::drop(std::mem::replace(data, self.0));
Ok(())
}
}
#[cfg(all(feature = "serde", feature = "base64"))]
impl<'a> serde::Deserialize<'a> for Clear {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
{
use base64::{engine::general_purpose::STANDARD as ENGINE, Engine};
use serde::de::Error;
let string = String::deserialize(deserializer)?;
let data = ENGINE.decode(string).map_err(D::Error::custom)?;
Ok(Clear(data.into()))
}
}
#[cfg(feature = "serde")]
impl<'a, 'b, T, S> serde::Deserialize<'a> for Secret<T, S>
where
T: TryFrom<Bytes<'b>>,
<T as TryFrom<Bytes<'b>>>::Error: std::fmt::Display,
S: LoadSecret + serde::Deserialize<'a>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'a>,
{
use serde::de::Error;
let loaded = S::deserialize(deserializer)?;
let mut buf = SecretBuf::with_capacity(loaded.size_hint());
loaded.load_secret(&mut buf).map_err(D::Error::custom)?;
let bytes = buf.into_bytes().try_into().map_err(D::Error::custom)?;
Ok(Secret::new(bytes))
}
}