Skip to main content

g_string/
gsecret.rs

1use core::{convert::Infallible, fmt::Debug, str::FromStr};
2
3use crate::{
4    DEFAULT_ASCII_ONLY, DEFAULT_MAX, DEFAULT_MIN, GString, GStringError, NoValidation, Validator,
5};
6
7/// `GSecret` is a type for containing secret.
8///
9/// Unlike `GString`, it's not Copy for security reason.
10///
11/// No Display implementation and debugging is redacted.
12///
13/// It Implements [zeroize](https://docs.rs/zeroize) and can be triggered manually or automatically on drop.
14///
15/// You can reveal the secret inside a closure to do something with it.
16///
17/// If you reveal and clone or take ownership of the revealed string, it goes beyond responsibility of GSecret to zeroize it.
18#[derive(Clone, PartialEq, Eq)]
19pub struct GSecret<
20    V: Validator = NoValidation,
21    const MIN: usize = DEFAULT_MIN,
22    const MAX: usize = DEFAULT_MAX,
23    const ASCII_ONLY: bool = DEFAULT_ASCII_ONLY,
24> {
25    inner: GString<V, MIN, MAX, ASCII_ONLY>,
26}
27
28impl GSecret {
29    /// Construct GSecret with default generic params.
30    #[inline]
31    pub fn try_default<S>(secret: S) -> Result<Self, GStringError<Infallible>>
32    where
33        S: AsRef<str>,
34    {
35        Ok(Self {
36            inner: GString::try_default(secret)?,
37        })
38    }
39}
40
41impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool>
42    GSecret<V, MIN, MAX, ASCII_ONLY>
43{
44    /// Construct GSecret with defined generic params.
45    #[inline]
46    pub fn try_new<S>(secret: S) -> Result<Self, GStringError<V::Err>>
47    where
48        S: AsRef<str>,
49    {
50        Ok(Self {
51            inner: GString::<V, MIN, MAX, ASCII_ONLY>::try_new(secret)?,
52        })
53    }
54
55    /// Reveals the secret.
56    ///
57    /// It's useful if you want to do something with the secret, e.g. authentication, authorization, etc.
58    ///
59    /// # WARNING
60    /// Revealing the secret will expose reference to string of the secret itself.
61    /// As long as you don't bring it outside of closure scope, it's fine.
62    /// If you bring it outside, it goes beyond responsibility of GSecret to zeroize it.
63    ///
64    pub fn reveal<R>(&self, func: impl FnOnce(&str) -> R) -> R {
65        func(self.inner.as_str())
66    }
67
68    /// Manually zeroize the secret.
69    ///
70    /// It's useful if you want to zeroize before the secret is out-of-scope/dopped.
71    #[inline]
72    pub fn zeroize(&mut self) {
73        self.inner.zeroize();
74    }
75}
76
77impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Drop
78    for GSecret<V, MIN, MAX, ASCII_ONLY>
79{
80    fn drop(&mut self) {
81        self.inner.zeroize();
82    }
83}
84
85impl<V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> zeroize::Zeroize
86    for GSecret<V, MIN, MAX, ASCII_ONLY>
87where
88    V: Validator,
89{
90    fn zeroize(&mut self) {
91        self.inner.zeroize();
92    }
93}
94
95impl<V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> zeroize::ZeroizeOnDrop
96    for GSecret<V, MIN, MAX, ASCII_ONLY>
97where
98    V: Validator,
99{
100}
101
102impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Debug
103    for GSecret<V, MIN, MAX, ASCII_ONLY>
104{
105    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
106        write!(f, "GSecret(<REDACTED>)")
107    }
108}
109
110impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> FromStr
111    for GSecret<V, MIN, MAX, ASCII_ONLY>
112{
113    type Err = GStringError<V::Err>;
114
115    fn from_str(s: &str) -> Result<Self, Self::Err> {
116        let secret = GSecret::try_new(s)?;
117
118        Ok(secret)
119    }
120}
121
122impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> TryFrom<&str>
123    for GSecret<V, MIN, MAX, ASCII_ONLY>
124{
125    type Error = GStringError<V::Err>;
126
127    fn try_from(value: &str) -> Result<Self, Self::Error> {
128        let secret = GSecret::try_new(value)?;
129
130        Ok(secret)
131    }
132}
133
134#[cfg(feature = "alloc")]
135extern crate alloc;
136
137#[cfg(feature = "alloc")]
138use alloc::string::String;
139
140#[cfg(feature = "alloc")]
141impl<V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> TryFrom<String>
142    for GSecret<V, MIN, MAX, ASCII_ONLY>
143where
144    V: Validator,
145{
146    type Error = GStringError<V::Err>;
147
148    fn try_from(mut value: String) -> Result<Self, Self::Error> {
149        use zeroize::Zeroize;
150
151        let secret = GSecret::try_new(&value);
152
153        value.zeroize();
154
155        secret
156    }
157}
158
159impl<
160    GSTRINGV: Validator,
161    GSECRETV: Validator,
162    const MIN: usize,
163    const MAX: usize,
164    const ASCII_ONLY: bool,
165> TryFrom<GString<GSTRINGV, MIN, MAX, ASCII_ONLY>> for GSecret<GSECRETV, MIN, MAX, ASCII_ONLY>
166{
167    type Error = GStringError<GSECRETV::Err>;
168
169    fn try_from(value: GString<GSTRINGV, MIN, MAX, ASCII_ONLY>) -> Result<Self, Self::Error> {
170        Self::try_new(value.as_str())
171    }
172}
173
174impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> core::hash::Hash
175    for GSecret<V, MIN, MAX, ASCII_ONLY>
176{
177    #[inline]
178    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
179        self.inner.hash(state)
180    }
181}