1use std::fmt;
9
10use secp256k1::SECP256K1;
11use secp256k1::schnorr::Signature;
12
13use super::{PublicKey, SecretKey, SecretKeyError};
14
15pub const SIGNATURE_SIZE: usize = 64;
17
18#[derive(Clone)]
34pub struct Keys {
35 secret_key: SecretKey,
36 public_key: PublicKey,
37 keypair: secp256k1::Keypair,
38}
39
40impl Keys {
41 #[must_use]
43 pub fn from_secret_key(secret_key: SecretKey) -> Self {
44 let keypair = secp256k1::Keypair::from_secret_key(SECP256K1, secret_key.as_inner());
45 let (xonly, _parity) = keypair.x_only_public_key();
46 Self {
47 secret_key,
48 public_key: PublicKey::from(xonly),
49 keypair,
50 }
51 }
52
53 pub fn generate() -> Result<Self, SecretKeyError> {
59 let secret_key = SecretKey::generate()?;
60 Ok(Self::from_secret_key(secret_key))
61 }
62
63 pub fn parse<S>(input: S) -> Result<Self, SecretKeyError>
69 where
70 S: AsRef<str>,
71 {
72 let secret_key = SecretKey::parse(input)?;
73 Ok(Self::from_secret_key(secret_key))
74 }
75
76 #[must_use]
78 pub const fn secret_key(&self) -> &SecretKey {
79 &self.secret_key
80 }
81
82 #[must_use]
84 pub const fn public_key(&self) -> &PublicKey {
85 &self.public_key
86 }
87
88 #[must_use]
92 pub const fn as_inner(&self) -> &secp256k1::Keypair {
93 &self.keypair
94 }
95
96 #[must_use]
102 pub fn sign_schnorr(&self, message: &[u8; 32]) -> Signature {
103 self.keypair.sign_schnorr(message)
104 }
105}
106
107impl fmt::Debug for Keys {
108 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
109 f.debug_struct("Keys")
110 .field("public_key", &self.public_key)
111 .field("secret_key", &"<redacted>")
112 .finish_non_exhaustive()
113 }
114}
115
116impl Drop for Keys {
117 fn drop(&mut self) {
118 self.keypair.non_secure_erase();
123 }
124}
125
126impl PartialEq for Keys {
127 fn eq(&self, other: &Self) -> bool {
128 self.secret_key == other.secret_key
129 }
130}
131
132impl Eq for Keys {}
133
134impl From<SecretKey> for Keys {
135 fn from(secret_key: SecretKey) -> Self {
136 Self::from_secret_key(secret_key)
137 }
138}
139
140#[cfg(test)]
141mod tests {
142 use hex_literal::hex;
143
144 use super::*;
145
146 const SECRET_HEX: &str = "0000000000000000000000000000000000000000000000000000000000000003";
148 const EXPECTED_PUBKEY: [u8; 32] =
149 hex!("F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9");
150
151 #[test]
152 fn derives_expected_public_key() {
153 let keys = Keys::parse(SECRET_HEX).unwrap();
154 assert_eq!(keys.public_key().to_byte_array(), EXPECTED_PUBKEY);
155 }
156
157 #[test]
158 fn generate_distinct() {
159 let lhs = Keys::generate().unwrap();
160 let rhs = Keys::generate().unwrap();
161 assert_ne!(lhs, rhs);
162 }
163
164 #[test]
165 fn signs_and_verifies() {
166 let keys = Keys::parse(SECRET_HEX).unwrap();
167 let message = hex!("0202020202020202020202020202020202020202020202020202020202020202");
168 let sig = keys.sign_schnorr(&message);
169 assert!(
170 SECP256K1
171 .verify_schnorr(&sig, &message, keys.public_key().as_inner())
172 .is_ok()
173 );
174 }
175
176 #[test]
177 fn debug_redacts_secret() {
178 let keys = Keys::parse(SECRET_HEX).unwrap();
179 let dbg = format!("{keys:?}");
180 assert!(dbg.contains("redacted"));
181 assert!(!dbg.contains(SECRET_HEX));
182 }
183
184 #[test]
185 fn equality_compares_secret_only() {
186 let lhs = Keys::parse(SECRET_HEX).unwrap();
187 let rhs = Keys::parse(SECRET_HEX).unwrap();
188 assert_eq!(lhs, rhs);
189 }
190}