cashu/
secret.rs

1//! Secret
2
3use 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/// The secret data that allows spending ecash
14#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
15#[serde(transparent)]
16pub struct Secret(String);
17
18/// Secret Errors
19#[derive(Debug, Error)]
20pub enum Error {
21    /// Invalid Length
22    #[error("Invalid secret length: `{0}`")]
23    InvalidLength(u64),
24    /// Hex Error
25    #[error(transparent)]
26    Hex(#[from] hex::Error),
27    /// Serde Json error
28    #[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    /// Create new [`Secret`]
40    #[inline]
41    pub fn new<S>(secret: S) -> Self
42    where
43        S: Into<String>,
44    {
45        Self(secret.into())
46    }
47
48    /// Create secret value
49    /// Generate a new random secret as the recommended 32 byte hex
50    pub fn generate() -> Self {
51        let mut rng = rand::thread_rng();
52
53        let mut random_bytes = [0u8; 32];
54
55        // Generate random bytes
56        rng.fill_bytes(&mut random_bytes);
57        // The secret string is hex encoded
58        let secret = hex::encode(random_bytes);
59        Self(secret)
60    }
61
62    /// [`Secret`] as bytes
63    #[inline]
64    pub fn as_bytes(&self) -> &[u8] {
65        self.0.as_bytes()
66    }
67
68    /// [`Secret`] to bytes
69    #[inline]
70    pub fn to_bytes(&self) -> Vec<u8> {
71        self.as_bytes().to_vec()
72    }
73
74    /// Check if secret is P2PK secret
75    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}