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