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 as_bytes(&self) -> &[u8] {
65 self.0.as_bytes()
66 }
67
68 #[inline]
70 pub fn to_bytes(&self) -> Vec<u8> {
71 self.as_bytes().to_vec()
72 }
73
74 pub fn is_p2pk(&self) -> bool {
76 use crate::nuts::Kind;
77
78 let secret: Result<crate::nuts::nut10::Secret, serde_json::Error> =
79 serde_json::from_str(&self.0);
80
81 if let Ok(secret) = secret {
82 if secret.kind().eq(&Kind::P2PK) {
83 return true;
84 }
85 }
86
87 false
88 }
89}
90
91impl FromStr for Secret {
92 type Err = Error;
93
94 fn from_str(s: &str) -> Result<Self, Self::Err> {
95 Ok(Self(s.to_string()))
96 }
97}
98
99impl fmt::Display for Secret {
100 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result {
101 write!(f, "{}", self.0)
102 }
103}
104
105impl From<Secret> for Vec<u8> {
106 fn from(value: Secret) -> Vec<u8> {
107 value.to_bytes()
108 }
109}
110
111impl From<&Secret> for Vec<u8> {
112 fn from(value: &Secret) -> Vec<u8> {
113 value.to_bytes()
114 }
115}
116
117impl TryFrom<Secret> for crate::nuts::nut10::Secret {
118 type Error = serde_json::Error;
119
120 fn try_from(unchecked_secret: Secret) -> Result<crate::nuts::nut10::Secret, Self::Error> {
121 serde_json::from_str(&unchecked_secret.0)
122 }
123}
124
125impl Drop for Secret {
126 fn drop(&mut self) {
127 self.0.zeroize();
128 }
129}
130
131impl TryFrom<&Secret> for crate::nuts::nut10::Secret {
132 type Error = Error;
133
134 fn try_from(unchecked_secret: &Secret) -> Result<crate::nuts::nut10::Secret, Self::Error> {
135 Ok(serde_json::from_str(&unchecked_secret.0)?)
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use std::assert_eq;
142 use std::str::FromStr;
143
144 use super::*;
145
146 #[test]
147 fn test_secret_from_str() {
148 let secret = Secret::generate();
149
150 let secret_str = secret.to_string();
151
152 assert_eq!(hex::decode(secret_str.clone()).unwrap().len(), 32);
153
154 let secret_n = Secret::from_str(&secret_str).unwrap();
155
156 assert_eq!(secret_n, secret)
157 }
158}