Skip to main content

cashu/nuts/nut01/
secret_key.rs

1use core::fmt;
2use core::ops::Deref;
3use core::str::FromStr;
4
5use bitcoin::hashes::sha256::Hash as Sha256Hash;
6use bitcoin::hashes::Hash;
7use bitcoin::secp256k1;
8use bitcoin::secp256k1::rand::rngs::OsRng;
9use bitcoin::secp256k1::schnorr::Signature;
10use bitcoin::secp256k1::{Keypair, Message, Scalar};
11use serde::de::Visitor;
12use serde::{Deserialize, Deserializer, Serialize};
13
14use super::{Error, PublicKey};
15use crate::SECP256K1;
16
17/// Secret key material.
18///
19/// Secret keys intentionally do not implement [`fmt::Display`]. Export secret
20/// material explicitly with [`Self::to_secret_hex`] when it is required by a
21/// persistence or interoperability boundary.
22///
23/// ```compile_fail
24/// use cashu::nuts::SecretKey;
25///
26/// let secret_key = SecretKey::generate();
27/// let _ = secret_key.to_string();
28/// ```
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct SecretKey {
31    inner: secp256k1::SecretKey,
32}
33
34impl Deref for SecretKey {
35    type Target = secp256k1::SecretKey;
36
37    fn deref(&self) -> &Self::Target {
38        &self.inner
39    }
40}
41
42impl From<secp256k1::SecretKey> for SecretKey {
43    fn from(inner: secp256k1::SecretKey) -> Self {
44        Self { inner }
45    }
46}
47
48impl SecretKey {
49    /// Parse from `bytes`
50    pub fn from_slice(slice: &[u8]) -> Result<Self, Error> {
51        Ok(Self {
52            inner: secp256k1::SecretKey::from_slice(slice)?,
53        })
54    }
55
56    /// Parse from `hex` string
57    pub fn from_hex<S>(hex: S) -> Result<Self, Error>
58    where
59        S: AsRef<str>,
60    {
61        Ok(Self {
62            inner: secp256k1::SecretKey::from_str(hex.as_ref())?,
63        })
64    }
65
66    /// Generate random secret key
67    pub fn generate() -> Self {
68        let (secret_key, _) = SECP256K1.generate_keypair(&mut OsRng);
69        Self { inner: secret_key }
70    }
71
72    /// Get secret key as `hex` string
73    pub fn to_secret_hex(&self) -> String {
74        self.inner.display_secret().to_string()
75    }
76
77    /// Get secret key as `bytes`
78    pub fn as_secret_bytes(&self) -> &[u8] {
79        self.inner.as_ref()
80    }
81
82    /// Get secret key as `bytes`
83    pub fn to_secret_bytes(&self) -> [u8; 32] {
84        self.inner.secret_bytes()
85    }
86
87    /// Schnorr Signature on Message
88    pub fn sign(&self, msg: &[u8]) -> Result<Signature, Error> {
89        let hash: Sha256Hash = Sha256Hash::hash(msg);
90        let msg = Message::from_digest_slice(hash.as_ref())?;
91        Ok(SECP256K1.sign_schnorr(&msg, &Keypair::from_secret_key(&SECP256K1, &self.inner)))
92    }
93
94    /// Get public key
95    pub fn public_key(&self) -> PublicKey {
96        self.inner.public_key(&SECP256K1).into()
97    }
98
99    /// [`SecretKey`] to [`Scalar`]
100    #[inline]
101    pub fn to_scalar(self) -> Scalar {
102        Scalar::from(self.inner)
103    }
104
105    /// [`SecretKey`] as [`Scalar`]
106    #[inline]
107    pub fn as_scalar(&self) -> Scalar {
108        Scalar::from(self.inner)
109    }
110}
111
112impl FromStr for SecretKey {
113    type Err = Error;
114
115    /// Try to parse [SecretKey] from `hex` or `bech32`
116    fn from_str(secret_key: &str) -> Result<Self, Self::Err> {
117        Self::from_hex(secret_key)
118    }
119}
120
121impl Serialize for SecretKey {
122    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
123        match serializer.is_human_readable() {
124            // For human-readable formats like JSON, serialize as hex string
125            true => serializer.serialize_str(&self.to_secret_hex()),
126            // For binary formats like CBOR, use the bytes serialization
127            false => serializer.serialize_bytes(self.as_secret_bytes()),
128        }
129    }
130}
131
132impl<'de> Deserialize<'de> for SecretKey {
133    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
134        match deserializer.is_human_readable() {
135            // For human-readable formats like JSON, deserialize from hex string
136            true => {
137                let secret_key: String = String::deserialize(deserializer)?;
138                SecretKey::from_hex(secret_key).map_err(serde::de::Error::custom)
139            }
140            // For binary formats like CBOR, use the bytes deserialization
141            false => {
142                struct SecretKeyVisitor;
143
144                impl Visitor<'_> for SecretKeyVisitor {
145                    type Value = SecretKey;
146
147                    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
148                        formatter.write_str("a byte array")
149                    }
150
151                    fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
152                    where
153                        E: serde::de::Error,
154                    {
155                        SecretKey::from_slice(value).map_err(serde::de::Error::custom)
156                    }
157                }
158
159                deserializer.deserialize_bytes(SecretKeyVisitor)
160            }
161        }
162    }
163}
164
165impl Drop for SecretKey {
166    fn drop(&mut self) {
167        self.inner.non_secure_erase();
168        tracing::trace!("Secret Key dropped.");
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn secret_hex_export_is_explicit() {
178        let hex = "50d7fd7aa2b2fe4607f41f4ce6f8794fc184dd47b8cdfbe4b3d1249aa02d35aa";
179        let secret_key = SecretKey::from_hex(hex).unwrap();
180
181        assert_eq!(secret_key.to_secret_hex(), hex);
182    }
183}