secure_gate/cloneable/
string.rs

1use crate::Dynamic;
2use zeroize::Zeroize;
3
4/// Inner wrapper for a string that can be safely cloned as a secret.
5///
6/// This struct wraps a `String` and implements the necessary traits for secure
7/// secret handling: `Clone` for duplication and `Zeroize` for secure memory wiping.
8/// The `zeroize(drop)` attribute ensures the string contents are zeroized when
9/// this struct is dropped.
10#[cfg(feature = "zeroize")]
11#[derive(Clone, Zeroize)]
12#[zeroize(drop)]
13pub struct CloneableStringInner(String);
14
15#[cfg(feature = "zeroize")]
16impl crate::CloneableSecretMarker for CloneableStringInner {}
17
18/// A string wrapped as a cloneable secret.
19///
20/// This type provides a secure wrapper around a `String` that can be safely cloned
21/// while ensuring the underlying data is properly zeroized when no longer needed.
22/// Use this for sensitive text data like passwords, tokens, or cryptographic passphrases.
23///
24/// # Examples
25///
26/// ```
27/// # #[cfg(feature = "zeroize")]
28/// # {
29/// use secure_gate::CloneableString;
30///
31/// // Create from a string
32/// let password: CloneableString = "secret123".to_string().into();
33///
34/// // Create from a string slice
35/// let token: CloneableString = "token_value".into();
36///
37/// // Access the inner string
38/// let inner = password.expose_inner();
39/// assert_eq!(inner.as_str(), "secret123");
40/// # }
41/// ```
42#[cfg(feature = "zeroize")]
43pub type CloneableString = Dynamic<CloneableStringInner>;
44
45#[cfg(feature = "zeroize")]
46impl CloneableString {
47    /// Returns a reference to the inner string without cloning.
48    ///
49    /// This method provides direct access to the wrapped `String`.
50    /// The reference is valid for the lifetime of the `CloneableString`.
51    #[inline(always)]
52    pub const fn expose_inner(&self) -> &String {
53        &self.expose_secret().0
54    }
55
56    /// Returns a mutable reference to the inner string.
57    ///
58    /// This method provides direct mutable access to the wrapped `String`.
59    /// Use this when you need to modify the string contents in-place.
60    #[inline(always)]
61    pub fn expose_inner_mut(&mut self) -> &mut String {
62        &mut self.expose_secret_mut().0
63    }
64
65    /// Construct a cloneable string secret by building it in a closure.
66    ///
67    /// This minimizes the time the secret spends on the stack:
68    /// - The closure builds a temporary `String`.
69    /// - It is immediately cloned to the heap.
70    /// - The temporary is zeroized before returning.
71    ///
72    /// Use this when reading passwords or tokens from user input.
73    ///
74    /// # Example
75    ///
76    /// ```
77    /// # #[cfg(feature = "zeroize")]
78    /// # {
79    /// use secure_gate::CloneableString;
80    /// use std::io::{self, Write};
81    ///
82    /// fn read_password() -> io::Result<String> {
83    ///     let mut input = String::new();
84    ///     io::stdout().flush()?;
85    ///     io::stdin().read_line(&mut input)?;
86    ///     Ok(input.trim_end().to_string())
87    /// }
88    ///
89    /// let pw = CloneableString::init_with(|| read_password().unwrap());
90    /// # }
91    /// ```
92    #[must_use]
93    pub fn init_with<F>(constructor: F) -> Self
94    where
95        F: FnOnce() -> String,
96    {
97        let mut tmp = constructor();
98        let secret = Self::from(tmp.clone());
99        tmp.zeroize();
100        secret
101    }
102
103    /// Fallible version of `init_with`.
104    ///
105    /// Same stack-minimization benefits as `init_with`, but allows for construction
106    /// that may fail with an error. Useful when reading secrets from fallible sources
107    /// like files, network connections, or user input that may encounter I/O errors.
108    pub fn try_init_with<F, E>(constructor: F) -> Result<Self, E>
109    where
110        F: FnOnce() -> Result<String, E>,
111    {
112        let mut tmp = constructor()?;
113        let secret = Self::from(tmp.clone());
114        tmp.zeroize();
115        Ok(secret)
116    }
117}
118
119#[cfg(feature = "zeroize")]
120impl From<String> for CloneableString {
121    fn from(value: String) -> Self {
122        Dynamic::new(CloneableStringInner(value))
123    }
124}
125
126#[cfg(feature = "zeroize")]
127impl From<&str> for CloneableString {
128    fn from(value: &str) -> Self {
129        Self::from(value.to_string())
130    }
131}