1use std::fmt;
4use std::str::FromStr;
5
6use bitcoin::secp256k1::rand::{self, RngCore};
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9use zeroize::Zeroize;
10
11use crate::util::hex;
12
13#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15#[serde(transparent)]
16pub struct Secret(String);
17
18#[derive(Debug, Error)]
20pub enum Error {
21 #[error("Invalid secret length: `{0}`")]
23 InvalidLength(u64),
24 #[error(transparent)]
26 Hex(#[from] hex::Error),
27 #[error(transparent)]
29 SerdeJsonError(#[from] serde_json::Error),
30}
31
32impl Default for Secret {
33 fn default() -> Self {
34 Self::generate()
35 }
36}
37
38impl Secret {
39 #[inline]
41 pub fn new<S>(secret: S) -> Self
42 where
43 S: Into<String>,
44 {
45 Self(secret.into())
46 }
47
48 pub fn generate() -> Self {
51 let mut rng = rand::thread_rng();
52
53 let mut random_bytes = [0u8; 32];
54
55 rng.fill_bytes(&mut random_bytes);
57 let secret = hex::encode(random_bytes);
59 Self(secret)
60 }
61
62 #[inline]
64 pub fn len(&self) -> usize {
65 self.0.len()
66 }
67
68 #[inline]
70 pub fn is_empty(&self) -> bool {
71 self.0.is_empty()
72 }
73
74 #[inline]
76 pub fn as_bytes(&self) -> &[u8] {
77 self.0.as_bytes()
78 }
79
80 #[inline]
82 pub fn to_bytes(&self) -> Vec<u8> {
83 self.as_bytes().to_vec()
84 }
85
86 pub fn is_p2pk(&self) -> bool {
88 use crate::nuts::Kind;
89
90 let secret: Result<crate::nuts::nut10::Secret, serde_json::Error> =
91 serde_json::from_str(&self.0);
92
93 if let Ok(secret) = secret {
94 if secret.kind().eq(&Kind::P2PK) {
95 return true;
96 }
97 }
98
99 false
100 }
101}
102
103impl FromStr for Secret {
104 type Err = Error;
105
106 fn from_str(s: &str) -> Result<Self, Self::Err> {
107 Ok(Self(s.to_string()))
108 }
109}
110
111impl fmt::Display for Secret {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
113 write!(f, "{}", self.0)
114 }
115}
116
117impl From<Secret> for Vec<u8> {
118 fn from(value: Secret) -> Vec<u8> {
119 value.to_bytes()
120 }
121}
122
123impl From<&Secret> for Vec<u8> {
124 fn from(value: &Secret) -> Vec<u8> {
125 value.to_bytes()
126 }
127}
128
129impl TryFrom<Secret> for crate::nuts::nut10::Secret {
130 type Error = serde_json::Error;
131
132 fn try_from(unchecked_secret: Secret) -> Result<crate::nuts::nut10::Secret, Self::Error> {
133 serde_json::from_str(&unchecked_secret.0)
134 }
135}
136
137impl Drop for Secret {
138 fn drop(&mut self) {
139 self.0.zeroize();
140 }
141}
142
143impl TryFrom<&Secret> for crate::nuts::nut10::Secret {
144 type Error = Error;
145
146 fn try_from(unchecked_secret: &Secret) -> Result<crate::nuts::nut10::Secret, Self::Error> {
147 Ok(serde_json::from_str(&unchecked_secret.0)?)
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use std::assert_eq;
154 use std::str::FromStr;
155
156 use super::*;
157
158 #[test]
159 fn test_secret_from_str() {
160 let secret = Secret::generate();
161
162 let secret_str = secret.to_string();
163
164 assert_eq!(hex::decode(secret_str.clone()).unwrap().len(), 32);
165
166 let secret_n = Secret::from_str(&secret_str).unwrap();
167
168 assert_eq!(secret_n, secret)
169 }
170
171 #[test]
172 fn test_is_p2pk_true() {
173 let p2pk_secret_str = r#"["P2PK",{"nonce":"5d11913ee0f92fefdc82a6764fd2457a","data":"026562efcfadc8e86d44da6a8adf80633d974302e62c850774db1fb36ff4cc7198"}]"#;
175 let secret = Secret::from_str(p2pk_secret_str).unwrap();
176
177 assert!(secret.is_p2pk());
178 }
179
180 #[test]
181 fn test_is_p2pk_false() {
182 let secret = Secret::generate();
184
185 assert!(!secret.is_p2pk());
186
187 let plain_secret = Secret::from_str("not_a_p2pk_secret").unwrap();
189
190 assert!(!plain_secret.is_p2pk());
191 }
192
193 #[test]
194 fn test_secret_to_vec_u8() {
195 let secret = Secret::from_str("test_secret_value").unwrap();
196
197 let bytes: Vec<u8> = secret.clone().into();
199 assert_eq!(bytes, b"test_secret_value".to_vec());
200 assert!(!bytes.is_empty());
201
202 let bytes_ref: Vec<u8> = (&secret).into();
204 assert_eq!(bytes_ref, b"test_secret_value".to_vec());
205 assert!(!bytes_ref.is_empty());
206 }
207}