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(pub 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, ExposeSecret};
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/// assert_eq!(password.expose_secret().0.as_str(), "secret123");
37/// # }
38/// ```
39pub type CloneableString = Dynamic<CloneableStringInner>;
40
41impl CloneableString {
42 /// Construct a cloneable string secret by building it in a closure.
43 ///
44 /// This minimizes the time the secret spends on the stack:
45 /// - The closure builds a temporary `String`.
46 /// - It is immediately cloned to the heap.
47 /// - The temporary is zeroized before returning.
48 ///
49 /// Use this when reading passwords or tokens from user input.
50 ///
51 /// # Example
52 ///
53 /// ```
54 /// # #[cfg(feature = "zeroize")]
55 /// # {
56 /// use secure_gate::CloneableString;
57 /// use std::io::{self, Write};
58 ///
59 /// fn read_password() -> io::Result<String> {
60 /// let mut input = String::new();
61 /// io::stdout().flush()?;
62 /// io::stdin().read_line(&mut input)?;
63 /// Ok(input.trim_end().to_string())
64 /// }
65 ///
66 /// let pw = CloneableString::init_with(|| read_password().unwrap());
67 /// # }
68 /// ```
69 #[must_use]
70 pub fn init_with<F>(constructor: F) -> Self
71 where
72 F: FnOnce() -> String,
73 {
74 let mut tmp = constructor();
75 let secret = Self::from(tmp.clone());
76 tmp.zeroize();
77 secret
78 }
79
80 /// Fallible version of `init_with`.
81 ///
82 /// Same stack-minimization benefits as `init_with`, but allows for construction
83 /// that may fail with an error. Useful when reading secrets from fallible sources
84 /// like files, network connections, or user input that may encounter I/O errors.
85 pub fn try_init_with<F, E>(constructor: F) -> Result<Self, E>
86 where
87 F: FnOnce() -> Result<String, E>,
88 {
89 let mut tmp = constructor()?;
90 let secret = Self::from(tmp.clone());
91 tmp.zeroize();
92 Ok(secret)
93 }
94}
95
96/// Wrap a `String` in a `CloneableString`.
97impl From<String> for CloneableString {
98 fn from(value: String) -> Self {
99 Dynamic::new(CloneableStringInner(value))
100 }
101}
102
103/// Wrap a string slice in a `CloneableString`.
104impl From<&str> for CloneableString {
105 fn from(value: &str) -> Self {
106 Self::from(value.to_string())
107 }
108}