Skip to main content

g_string/
gsecret.rs

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