otp_std/secret/
core.rs

1//! Core types and functions for working with secrets.
2
3use std::{
4    borrow::Cow,
5    fmt,
6    hash::{Hash, Hasher},
7    str::FromStr,
8};
9
10use constant_time_eq::constant_time_eq;
11use miette::Diagnostic;
12
13#[cfg(feature = "serde")]
14use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
15
16use thiserror::Error;
17
18use crate::secret::{
19    encoding,
20    length::{self, Length},
21};
22
23#[cfg(feature = "generate-secret")]
24use crate::secret::generate::generate;
25
26/// Represents secrets.
27#[derive(Debug, Clone)]
28pub struct Secret<'s> {
29    value: Cow<'s, [u8]>,
30}
31
32#[cfg(feature = "serde")]
33impl Serialize for Secret<'_> {
34    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
35        self.encode().serialize(serializer)
36    }
37}
38
39#[cfg(feature = "serde")]
40impl<'de> Deserialize<'de> for Secret<'_> {
41    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
42        let string = <&str>::deserialize(deserializer)?;
43
44        Self::decode(string).map_err(de::Error::custom)
45    }
46}
47
48impl fmt::Display for Secret<'_> {
49    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50        self.encode().fmt(formatter)
51    }
52}
53
54impl PartialEq for Secret<'_> {
55    fn eq(&self, other: &Self) -> bool {
56        constant_time_eq(self.as_bytes(), other.as_bytes())
57    }
58}
59
60impl Eq for Secret<'_> {}
61
62impl Hash for Secret<'_> {
63    fn hash<H: Hasher>(&self, state: &mut H) {
64        self.as_bytes().hash(state);
65    }
66}
67
68impl AsRef<[u8]> for Secret<'_> {
69    fn as_ref(&self) -> &[u8] {
70        self.as_bytes()
71    }
72}
73
74/// Represents sources of errors that can occur when decoding secrets.
75#[derive(Debug, Error, Diagnostic)]
76#[error(transparent)]
77#[diagnostic(transparent)]
78pub enum ErrorSource {
79    /// Secret has an unsafe length.
80    Length(#[from] length::Error),
81    /// Secret could not be decoded.
82    Encoding(#[from] encoding::Error),
83}
84
85/// Represents errors that can occur when decoding secrets.
86#[derive(Debug, Error, Diagnostic)]
87#[error("failed to decode secret")]
88#[diagnostic(code(otp_std::secret), help("make sure the secret is valid"))]
89pub struct Error {
90    /// The source of this error.
91    #[source]
92    #[diagnostic_source]
93    pub source: ErrorSource,
94}
95
96impl Error {
97    /// Constructs [`Self`].
98    pub const fn new(source: ErrorSource) -> Self {
99        Self { source }
100    }
101
102    /// Constructs [`Self`] from [`length::Error`].
103    pub fn length(error: length::Error) -> Self {
104        Self::new(error.into())
105    }
106
107    /// Constructs [`Self`] from [`encoding::Error`].
108    pub fn encoding(error: encoding::Error) -> Self {
109        Self::new(error.into())
110    }
111}
112
113impl<'s> Secret<'s> {
114    /// Constructs [`Self`], if possible.
115    ///
116    /// # Errors
117    ///
118    /// Returns [`length::Error`] if the secret has an unsafe length.
119    pub fn new(value: Cow<'s, [u8]>) -> Result<Self, length::Error> {
120        Length::check(value.len())?;
121
122        // SAFETY: the value has valid length for `Self`
123        Ok(unsafe { Self::new_unchecked(value) })
124    }
125
126    /// Constructs [`Self`] without checking the secret length.
127    ///
128    /// # Safety
129    ///
130    /// The caller must ensure that the secret length is safe.
131    pub const unsafe fn new_unchecked(value: Cow<'s, [u8]>) -> Self {
132        Self { value }
133    }
134
135    /// Constructs [`Self`] from borrowed data, if possible.
136    ///
137    /// # Errors
138    ///
139    /// Returns [`length::Error`] if the secret has an unsafe length.
140    pub fn borrowed(value: &'s [u8]) -> Result<Self, length::Error> {
141        Self::new(Cow::Borrowed(value))
142    }
143
144    /// Constructs [`Self`] from borrowed data without checking the secret length.
145    ///
146    /// # Safety
147    ///
148    /// The caller must ensure that the secret length is safe.
149    pub const unsafe fn borrowed_unchecked(value: &'s [u8]) -> Self {
150        Self::new_unchecked(Cow::Borrowed(value))
151    }
152
153    /// Constructs [`Self`] from owned data, if possible.
154    ///
155    /// # Errors
156    ///
157    /// Returns [`length::Error`] if the secret has an unsafe length.
158    pub fn owned(value: Vec<u8>) -> Result<Self, length::Error> {
159        Self::new(Cow::Owned(value))
160    }
161
162    /// Constructs [`Self`] from owned data without checking the secret length.
163    ///
164    /// # Safety
165    ///
166    /// The caller must ensure that the secret length is safe.
167    pub const unsafe fn owned_unchecked(value: Vec<u8>) -> Self {
168        Self::new_unchecked(Cow::Owned(value))
169    }
170
171    /// Consumes [`Self`] and returns the contained secret value.
172    pub fn get(self) -> Cow<'s, [u8]> {
173        self.value
174    }
175}
176
177impl Secret<'_> {
178    /// Returns the secret value as bytes.
179    pub fn as_bytes(&self) -> &[u8] {
180        self.value.as_ref()
181    }
182
183    /// Decodes [`Self`] from the given string.
184    ///
185    /// # Errors
186    ///
187    /// Returns [`struct@Error`] if the secret could not be decoded.
188    /// This can happen if the string is invalid or the resulting length is unsafe.
189    pub fn decode<S: AsRef<str>>(string: S) -> Result<Self, Error> {
190        let owned = encoding::decode(string).map_err(Error::encoding)?;
191
192        let secret = Self::owned(owned).map_err(Error::length)?;
193
194        Ok(secret)
195    }
196
197    /// Encodes [`Self`] into [`String`].
198    pub fn encode(&self) -> String {
199        encoding::encode(self.as_bytes())
200    }
201}
202
203impl FromStr for Secret<'_> {
204    type Err = Error;
205
206    fn from_str(string: &str) -> Result<Self, Self::Err> {
207        Self::decode(string)
208    }
209}
210
211#[cfg(feature = "generate-secret")]
212impl Secret<'_> {
213    /// Generates secrets of the given length.
214    pub fn generate(length: Length) -> Self {
215        unsafe { Self::owned_unchecked(generate(length)) }
216    }
217
218    /// Generates secrets of default length.
219    pub fn generate_default() -> Self {
220        Self::generate(Length::default())
221    }
222}
223
224#[cfg(feature = "generate-secret")]
225impl Default for Secret<'_> {
226    fn default() -> Self {
227        Self::generate_default()
228    }
229}
230
231/// Represents owned [`Secret`].
232pub type Owned = Secret<'static>;
233
234impl Secret<'_> {
235    /// Converts [`Self`] into [`Owned`].
236    pub fn into_owned(self) -> Owned {
237        // SAFETY: the contained secret is valid (by construction)
238        unsafe { Owned::owned_unchecked(self.get().into_owned()) }
239    }
240}