1use 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#[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#[derive(Debug, Error, Diagnostic)]
76#[error(transparent)]
77#[diagnostic(transparent)]
78pub enum ErrorSource {
79 Length(#[from] length::Error),
81 Encoding(#[from] encoding::Error),
83}
84
85#[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 #[source]
92 #[diagnostic_source]
93 pub source: ErrorSource,
94}
95
96impl Error {
97 pub const fn new(source: ErrorSource) -> Self {
99 Self { source }
100 }
101
102 pub fn length(error: length::Error) -> Self {
104 Self::new(error.into())
105 }
106
107 pub fn encoding(error: encoding::Error) -> Self {
109 Self::new(error.into())
110 }
111}
112
113impl<'s> Secret<'s> {
114 pub fn new(value: Cow<'s, [u8]>) -> Result<Self, length::Error> {
120 Length::check(value.len())?;
121
122 Ok(unsafe { Self::new_unchecked(value) })
124 }
125
126 pub const unsafe fn new_unchecked(value: Cow<'s, [u8]>) -> Self {
132 Self { value }
133 }
134
135 pub fn borrowed(value: &'s [u8]) -> Result<Self, length::Error> {
141 Self::new(Cow::Borrowed(value))
142 }
143
144 pub const unsafe fn borrowed_unchecked(value: &'s [u8]) -> Self {
150 Self::new_unchecked(Cow::Borrowed(value))
151 }
152
153 pub fn owned(value: Vec<u8>) -> Result<Self, length::Error> {
159 Self::new(Cow::Owned(value))
160 }
161
162 pub const unsafe fn owned_unchecked(value: Vec<u8>) -> Self {
168 Self::new_unchecked(Cow::Owned(value))
169 }
170
171 pub fn get(self) -> Cow<'s, [u8]> {
173 self.value
174 }
175}
176
177impl Secret<'_> {
178 pub fn as_bytes(&self) -> &[u8] {
180 self.value.as_ref()
181 }
182
183 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 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 pub fn generate(length: Length) -> Self {
215 unsafe { Self::owned_unchecked(generate(length)) }
216 }
217
218 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
231pub type Owned = Secret<'static>;
233
234impl Secret<'_> {
235 pub fn into_owned(self) -> Owned {
237 unsafe { Owned::owned_unchecked(self.get().into_owned()) }
239 }
240}