#[cfg(feature = "alloc")]
extern crate alloc;
use alloc::boxed::Box;
use zeroize::Zeroize;
#[cfg(any(
feature = "encoding-hex",
feature = "encoding-base64",
feature = "encoding-bech32",
feature = "encoding-bech32m",
feature = "ct-eq",
feature = "std",
))]
use crate::RevealSecret;
#[cfg(feature = "encoding-base64")]
use crate::traits::encoding::base64_url::ToBase64Url;
#[cfg(feature = "encoding-bech32")]
use crate::traits::encoding::bech32::ToBech32;
#[cfg(feature = "encoding-bech32m")]
use crate::traits::encoding::bech32m::ToBech32m;
#[cfg(feature = "encoding-hex")]
use crate::traits::encoding::hex::ToHex;
#[cfg(feature = "rand")]
use rand::{TryCryptoRng, TryRng, rngs::SysRng};
#[cfg(feature = "encoding-base64")]
use crate::traits::decoding::base64_url::FromBase64UrlStr;
#[cfg(feature = "encoding-bech32")]
use crate::traits::decoding::bech32::FromBech32Str;
#[cfg(feature = "encoding-bech32m")]
use crate::traits::decoding::bech32m::FromBech32mStr;
#[cfg(feature = "encoding-hex")]
use crate::traits::decoding::hex::FromHexStr;
pub struct Dynamic<T: ?Sized + zeroize::Zeroize> {
inner: Box<T>,
}
impl<T: ?Sized + zeroize::Zeroize> Dynamic<T> {
#[doc(alias = "from")]
#[inline(always)]
pub fn new<U>(value: U) -> Self
where
U: Into<Box<T>>,
{
let inner = value.into();
Self { inner }
}
}
impl<T: ?Sized + zeroize::Zeroize> From<Box<T>> for Dynamic<T> {
#[inline(always)]
fn from(boxed: Box<T>) -> Self {
Self { inner: boxed }
}
}
impl From<&[u8]> for Dynamic<Vec<u8>> {
#[inline(always)]
fn from(slice: &[u8]) -> Self {
Self::new(slice.to_vec())
}
}
impl From<&str> for Dynamic<String> {
#[inline(always)]
fn from(input: &str) -> Self {
Self::new(input.to_string())
}
}
impl<T: 'static + zeroize::Zeroize> From<T> for Dynamic<T> {
#[inline(always)]
fn from(value: T) -> Self {
Self {
inner: Box::new(value),
}
}
}
#[cfg(feature = "encoding-hex")]
impl Dynamic<Vec<u8>> {
#[inline]
pub fn to_hex(&self) -> alloc::string::String {
self.with_secret(|s: &Vec<u8>| s.to_hex())
}
#[inline]
pub fn to_hex_upper(&self) -> alloc::string::String {
self.with_secret(|s: &Vec<u8>| s.to_hex_upper())
}
#[inline]
pub fn to_hex_zeroizing(&self) -> crate::EncodedSecret {
self.with_secret(|s: &Vec<u8>| s.to_hex_zeroizing())
}
#[inline]
pub fn to_hex_upper_zeroizing(&self) -> crate::EncodedSecret {
self.with_secret(|s: &Vec<u8>| s.to_hex_upper_zeroizing())
}
pub fn try_from_hex(s: &str) -> Result<Self, crate::error::HexError> {
Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(
s.try_from_hex()?,
)))
}
}
#[cfg(feature = "encoding-base64")]
impl Dynamic<Vec<u8>> {
#[inline]
pub fn to_base64url(&self) -> alloc::string::String {
self.with_secret(|s: &Vec<u8>| s.to_base64url())
}
#[inline]
pub fn to_base64url_zeroizing(&self) -> crate::EncodedSecret {
self.with_secret(|s: &Vec<u8>| s.to_base64url_zeroizing())
}
pub fn try_from_base64url(s: &str) -> Result<Self, crate::error::Base64Error> {
Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(
s.try_from_base64url()?,
)))
}
}
#[cfg(feature = "encoding-bech32")]
impl Dynamic<Vec<u8>> {
#[inline]
pub fn try_to_bech32(
&self,
hrp: &str,
) -> Result<alloc::string::String, crate::error::Bech32Error> {
self.with_secret(|s: &Vec<u8>| s.try_to_bech32(hrp))
}
#[inline]
pub fn try_to_bech32_zeroizing(
&self,
hrp: &str,
) -> Result<crate::EncodedSecret, crate::error::Bech32Error> {
self.with_secret(|s: &Vec<u8>| s.try_to_bech32_zeroizing(hrp))
}
pub fn try_from_bech32(s: &str, expected_hrp: &str) -> Result<Self, crate::error::Bech32Error> {
Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(
s.try_from_bech32(expected_hrp)?,
)))
}
pub fn try_from_bech32_unchecked(s: &str) -> Result<Self, crate::error::Bech32Error> {
let (_hrp, bytes) = s.try_from_bech32_unchecked()?;
Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(bytes)))
}
}
#[cfg(feature = "encoding-bech32m")]
impl Dynamic<Vec<u8>> {
#[inline]
pub fn try_to_bech32m(
&self,
hrp: &str,
) -> Result<alloc::string::String, crate::error::Bech32Error> {
self.with_secret(|s: &Vec<u8>| s.try_to_bech32m(hrp))
}
#[inline]
pub fn try_to_bech32m_zeroizing(
&self,
hrp: &str,
) -> Result<crate::EncodedSecret, crate::error::Bech32Error> {
self.with_secret(|s: &Vec<u8>| s.try_to_bech32m_zeroizing(hrp))
}
pub fn try_from_bech32m(
s: &str,
expected_hrp: &str,
) -> Result<Self, crate::error::Bech32Error> {
Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(
s.try_from_bech32m(expected_hrp)?,
)))
}
pub fn try_from_bech32m_unchecked(s: &str) -> Result<Self, crate::error::Bech32Error> {
let (_hrp, bytes) = s.try_from_bech32m_unchecked()?;
Ok(Self::from_protected_bytes(zeroize::Zeroizing::new(bytes)))
}
}
impl Dynamic<Vec<u8>> {
#[cfg(any(
feature = "encoding-hex",
feature = "encoding-base64",
feature = "encoding-bech32",
feature = "encoding-bech32m",
))]
#[inline(always)]
fn from_protected_bytes(mut protected: zeroize::Zeroizing<alloc::vec::Vec<u8>>) -> Self {
let mut boxed = Box::<alloc::vec::Vec<u8>>::default();
core::mem::swap(&mut *boxed, &mut *protected);
Self::from(boxed)
}
#[inline(always)]
pub fn new_with<F>(f: F) -> Self
where
F: FnOnce(&mut alloc::vec::Vec<u8>),
{
let mut v = alloc::vec::Vec::new();
f(&mut v);
Self::new(v)
}
}
impl Dynamic<alloc::string::String> {
#[inline(always)]
pub fn new_with<F>(f: F) -> Self
where
F: FnOnce(&mut alloc::string::String),
{
let mut s = alloc::string::String::new();
f(&mut s);
Self::new(s)
}
}
impl crate::RevealSecret for Dynamic<String> {
type Inner = String;
#[inline(always)]
fn with_secret<F, R>(&self, f: F) -> R
where
F: FnOnce(&String) -> R,
{
f(&self.inner)
}
#[inline(always)]
fn expose_secret(&self) -> &String {
&self.inner
}
#[inline(always)]
fn len(&self) -> usize {
self.inner.len()
}
#[inline(always)]
fn into_inner(mut self) -> crate::InnerSecret<String>
where
Self: Sized,
Self::Inner: Sized + Default + zeroize::Zeroize,
{
let boxed = core::mem::replace(&mut self.inner, Box::new(String::new()));
crate::InnerSecret::new(*boxed)
}
}
impl<T: zeroize::Zeroize> crate::RevealSecret for Dynamic<Vec<T>> {
type Inner = Vec<T>;
#[inline(always)]
fn with_secret<F, R>(&self, f: F) -> R
where
F: FnOnce(&Vec<T>) -> R,
{
f(&self.inner)
}
#[inline(always)]
fn expose_secret(&self) -> &Vec<T> {
&self.inner
}
#[inline(always)]
fn len(&self) -> usize {
self.inner.len() * core::mem::size_of::<T>()
}
#[inline(always)]
fn into_inner(mut self) -> crate::InnerSecret<Vec<T>>
where
Self: Sized,
Self::Inner: Sized + Default + zeroize::Zeroize,
{
let boxed = core::mem::replace(&mut self.inner, Box::new(Vec::new()));
crate::InnerSecret::new(*boxed)
}
}
impl crate::RevealSecretMut for Dynamic<String> {
#[inline(always)]
fn with_secret_mut<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut String) -> R,
{
f(&mut self.inner)
}
#[inline(always)]
fn expose_secret_mut(&mut self) -> &mut String {
&mut self.inner
}
}
impl<T: zeroize::Zeroize> crate::RevealSecretMut for Dynamic<Vec<T>> {
#[inline(always)]
fn with_secret_mut<F, R>(&mut self, f: F) -> R
where
F: FnOnce(&mut Vec<T>) -> R,
{
f(&mut self.inner)
}
#[inline(always)]
fn expose_secret_mut(&mut self) -> &mut Vec<T> {
&mut self.inner
}
}
#[cfg(feature = "rand")]
impl Dynamic<alloc::vec::Vec<u8>> {
#[inline]
pub fn from_random(len: usize) -> Self {
Self::new_with(|v| {
v.resize(len, 0u8);
SysRng
.try_fill_bytes(v)
.expect("SysRng failure is a program error");
})
}
#[inline]
pub fn from_rng<R: TryRng + TryCryptoRng>(len: usize, rng: &mut R) -> Result<Self, R::Error> {
let mut result = Ok(());
let this = Self::new_with(|v| {
v.resize(len, 0u8);
result = rng.try_fill_bytes(v);
});
result.map(|_| this)
}
}
#[cfg(feature = "ct-eq")]
impl<T: ?Sized + zeroize::Zeroize> crate::ConstantTimeEq for Dynamic<T>
where
T: crate::ConstantTimeEq,
Self: crate::RevealSecret<Inner = T>,
{
fn ct_eq(&self, other: &Self) -> bool {
self.expose_secret().ct_eq(other.expose_secret())
}
}
impl<T: ?Sized + zeroize::Zeroize> core::fmt::Debug for Dynamic<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("[REDACTED]")
}
}
#[cfg(feature = "cloneable")]
impl<T: zeroize::Zeroize + crate::CloneableSecret> Clone for Dynamic<T> {
fn clone(&self) -> Self {
Self::new(self.inner.clone())
}
}
#[cfg(feature = "std")]
impl std::io::Write for Dynamic<alloc::vec::Vec<u8>> {
#[inline]
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
use crate::RevealSecretMut;
self.with_secret_mut(|v| std::io::Write::write(v, buf))
}
#[inline]
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[cfg(feature = "std")]
pub struct DynamicReader<'a> {
secret: &'a Dynamic<alloc::vec::Vec<u8>>,
offset: usize,
}
#[cfg(feature = "std")]
impl std::io::Read for DynamicReader<'_> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let offset = self.offset;
let n = self.secret.with_secret(|v| {
let remaining = v.len().saturating_sub(offset);
let n = remaining.min(buf.len());
buf[..n].copy_from_slice(&v[offset..offset + n]);
n
});
self.offset += n;
Ok(n)
}
}
#[cfg(feature = "std")]
impl Dynamic<alloc::vec::Vec<u8>> {
#[inline]
pub fn as_reader(&self) -> DynamicReader<'_> {
DynamicReader {
secret: self,
offset: 0,
}
}
}
#[cfg(feature = "serde-serialize")]
impl<T: zeroize::Zeroize + crate::SerializableSecret> serde::Serialize for Dynamic<T> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.inner.serialize(serializer)
}
}
#[cfg(feature = "serde-deserialize")]
pub const MAX_DESERIALIZE_BYTES: usize = 1_048_576;
#[cfg(feature = "serde-deserialize")]
impl Dynamic<alloc::vec::Vec<u8>> {
pub fn deserialize_with_limit<'de, D>(deserializer: D, limit: usize) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut buf: zeroize::Zeroizing<alloc::vec::Vec<u8>> =
zeroize::Zeroizing::new(serde::Deserialize::deserialize(deserializer)?);
if buf.len() > limit {
return Err(serde::de::Error::custom(
"deserialized secret exceeds maximum size",
));
}
let mut boxed = Box::<alloc::vec::Vec<u8>>::default();
core::mem::swap(&mut *boxed, &mut *buf);
Ok(Self::from(boxed))
}
}
#[cfg(feature = "serde-deserialize")]
impl Dynamic<String> {
pub fn deserialize_with_limit<'de, D>(deserializer: D, limit: usize) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let mut buf: zeroize::Zeroizing<alloc::string::String> =
zeroize::Zeroizing::new(serde::Deserialize::deserialize(deserializer)?);
if buf.len() > limit {
return Err(serde::de::Error::custom(
"deserialized secret exceeds maximum size",
));
}
let mut boxed = Box::<alloc::string::String>::default();
core::mem::swap(&mut *boxed, &mut *buf);
Ok(Self::from(boxed))
}
}
#[cfg(feature = "serde-deserialize")]
impl<'de> serde::Deserialize<'de> for Dynamic<alloc::vec::Vec<u8>> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Self::deserialize_with_limit(deserializer, MAX_DESERIALIZE_BYTES)
}
}
#[cfg(feature = "serde-deserialize")]
impl<'de> serde::Deserialize<'de> for Dynamic<String> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
Self::deserialize_with_limit(deserializer, MAX_DESERIALIZE_BYTES)
}
}
impl<T: ?Sized + zeroize::Zeroize> zeroize::Zeroize for Dynamic<T> {
fn zeroize(&mut self) {
self.inner.zeroize();
}
}
impl<T: ?Sized + zeroize::Zeroize> Drop for Dynamic<T> {
fn drop(&mut self) {
self.zeroize();
}
}
impl<T: ?Sized + zeroize::Zeroize> zeroize::ZeroizeOnDrop for Dynamic<T> {}