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/dropped.
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    #[inline]
91    fn zeroize(&mut self) {
92        self.inner.zeroize();
93    }
94}
95
96impl<V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> zeroize::ZeroizeOnDrop
97    for GSecret<V, MIN, MAX, ASCII_ONLY>
98where
99    V: Validator,
100{
101}
102
103impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> Debug
104    for GSecret<V, MIN, MAX, ASCII_ONLY>
105{
106    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
107        write!(f, "GSecret(<REDACTED>)")
108    }
109}
110
111impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> FromStr
112    for GSecret<V, MIN, MAX, ASCII_ONLY>
113{
114    type Err = GStringError<V::Err>;
115
116    fn from_str(s: &str) -> Result<Self, Self::Err> {
117        let secret = GSecret::try_new(s)?;
118
119        Ok(secret)
120    }
121}
122
123impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> TryFrom<&str>
124    for GSecret<V, MIN, MAX, ASCII_ONLY>
125{
126    type Error = GStringError<V::Err>;
127
128    fn try_from(value: &str) -> Result<Self, Self::Error> {
129        let secret = GSecret::try_new(value)?;
130
131        Ok(secret)
132    }
133}
134
135#[cfg(feature = "alloc")]
136extern crate alloc;
137
138#[cfg(feature = "alloc")]
139use alloc::string::String;
140
141#[cfg(feature = "alloc")]
142impl<V, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> TryFrom<String>
143    for GSecret<V, MIN, MAX, ASCII_ONLY>
144where
145    V: Validator,
146{
147    type Error = GStringError<V::Err>;
148
149    fn try_from(mut value: String) -> Result<Self, Self::Error> {
150        use zeroize::Zeroize;
151
152        let secret = GSecret::try_new(&value);
153
154        value.zeroize();
155
156        secret
157    }
158}
159
160impl<
161    GSTRINGV: Validator,
162    GSECRETV: Validator,
163    const MIN: usize,
164    const MAX: usize,
165    const ASCII_ONLY: bool,
166> TryFrom<GString<GSTRINGV, MIN, MAX, ASCII_ONLY>> for GSecret<GSECRETV, MIN, MAX, ASCII_ONLY>
167{
168    type Error = GStringError<GSECRETV::Err>;
169
170    fn try_from(value: GString<GSTRINGV, MIN, MAX, ASCII_ONLY>) -> Result<Self, Self::Error> {
171        Self::try_new(value.as_str())
172    }
173}
174
175impl<V: Validator, const MIN: usize, const MAX: usize, const ASCII_ONLY: bool> core::hash::Hash
176    for GSecret<V, MIN, MAX, ASCII_ONLY>
177{
178    #[inline]
179    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
180        self.inner.hash(state)
181    }
182}