key_parsing.rs (7601B)
1 use std::collections::HashMap; 2 use std::str::FromStr; 3 4 use crate::Error; 5 use ehttp::{Request, Response}; 6 use enostr::{Keypair, Pubkey, SecretKey}; 7 use poll_promise::Promise; 8 use serde::{Deserialize, Serialize}; 9 use tracing::error; 10 11 #[derive(Debug, PartialEq, Clone)] 12 pub enum AcquireKeyError { 13 InvalidKey, 14 Nip05Failed(String), 15 } 16 17 impl std::fmt::Display for AcquireKeyError { 18 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 19 match self { 20 AcquireKeyError::InvalidKey => write!(f, "The inputted key is invalid."), 21 AcquireKeyError::Nip05Failed(e) => { 22 write!(f, "Failed to get pubkey from Nip05 address: {e}") 23 } 24 } 25 } 26 } 27 28 impl std::error::Error for AcquireKeyError {} 29 30 #[derive(Deserialize, Serialize)] 31 pub struct Nip05Result { 32 pub names: HashMap<String, String>, 33 pub relays: Option<HashMap<String, Vec<String>>>, 34 } 35 36 fn parse_nip05_response(response: Response) -> Result<Nip05Result, Error> { 37 serde_json::from_slice::<Nip05Result>(&response.bytes) 38 .map_err(|e| Error::Generic(e.to_string())) 39 } 40 41 fn get_pubkey_from_result(result: Nip05Result, user: String) -> Result<Pubkey, Error> { 42 match result.names.get(&user).to_owned() { 43 Some(pubkey_str) => Pubkey::from_hex(pubkey_str).map_err(|e| { 44 Error::Generic("Could not parse pubkey: ".to_string() + e.to_string().as_str()) 45 }), 46 None => Err(Error::Generic("Could not find user in json.".to_string())), 47 } 48 } 49 50 fn get_nip05_pubkey(id: &str) -> Promise<Result<Pubkey, Error>> { 51 let (sender, promise) = Promise::new(); 52 let mut parts = id.split('@'); 53 54 let user = match parts.next() { 55 Some(user) => user, 56 None => { 57 sender.send(Err(Error::Generic( 58 "Address does not contain username.".to_string(), 59 ))); 60 return promise; 61 } 62 }; 63 let host = match parts.next() { 64 Some(host) => host, 65 None => { 66 sender.send(Err(Error::Generic( 67 "Nip05 address does not contain host.".to_string(), 68 ))); 69 return promise; 70 } 71 }; 72 73 if parts.next().is_some() { 74 sender.send(Err(Error::Generic( 75 "Nip05 address contains extraneous parts.".to_string(), 76 ))); 77 return promise; 78 } 79 80 let url = format!("https://{host}/.well-known/nostr.json?name={user}"); 81 let request = Request::get(url); 82 83 let cloned_user = user.to_string(); 84 ehttp::fetch(request, move |response: Result<Response, String>| { 85 let result = match response { 86 Ok(resp) => parse_nip05_response(resp) 87 .and_then(move |result| get_pubkey_from_result(result, cloned_user)), 88 Err(e) => Err(Error::Generic(e.to_string())), 89 }; 90 sender.send(result); 91 }); 92 93 promise 94 } 95 96 fn retrieving_nip05_pubkey(key: &str) -> bool { 97 key.contains('@') 98 } 99 100 fn nip05_promise_wrapper(id: &str) -> Promise<Result<Keypair, AcquireKeyError>> { 101 let (sender, promise) = Promise::new(); 102 let original_promise = get_nip05_pubkey(id); 103 104 std::thread::spawn(move || { 105 let result = original_promise.block_and_take(); 106 let transformed_result = match result { 107 Ok(public_key) => Ok(Keypair::only_pubkey(public_key)), 108 Err(e) => { 109 error!("Nip05 Failed: {e}"); 110 Err(AcquireKeyError::Nip05Failed(e.to_string())) 111 } 112 }; 113 sender.send(transformed_result); 114 }); 115 116 promise 117 } 118 119 /// Attempts to turn a string slice key from the user into a Nostr-Sdk Keypair object. 120 /// The `key` can be in any of the following formats: 121 /// - Public Bech32 key (prefix "npub"): "npub1xyz..." 122 /// - Private Bech32 key (prefix "nsec"): "nsec1xyz..." 123 /// - Public hex key: "02a1..." 124 /// - Private hex key: "5dab..." 125 /// - NIP-05 address: "example@nostr.com" 126 /// 127 pub fn perform_key_retrieval(key: &str) -> Promise<Result<Keypair, AcquireKeyError>> { 128 let tmp_key: &str = if let Some(stripped) = key.strip_prefix('@') { 129 stripped 130 } else { 131 key 132 }; 133 134 if retrieving_nip05_pubkey(tmp_key) { 135 nip05_promise_wrapper(tmp_key) 136 } else { 137 let res = if let Ok(pubkey) = Pubkey::try_from_bech32_string(tmp_key, true) { 138 Ok(Keypair::only_pubkey(pubkey)) 139 } else if let Ok(pubkey) = Pubkey::try_from_hex_str_with_verify(tmp_key) { 140 Ok(Keypair::only_pubkey(pubkey)) 141 } else if let Ok(secret_key) = SecretKey::from_str(tmp_key) { 142 Ok(Keypair::from_secret(secret_key)) 143 } else { 144 Err(AcquireKeyError::InvalidKey) 145 }; 146 147 Promise::from_ready(res) 148 } 149 } 150 151 #[cfg(test)] 152 mod tests { 153 use super::*; 154 use crate::promise_assert; 155 156 #[test] 157 fn test_pubkey() { 158 let pubkey_str = "npub1xtscya34g58tk0z605fvr788k263gsu6cy9x0mhnm87echrgufzsevkk5s"; 159 let expected_pubkey = 160 Pubkey::try_from_bech32_string(pubkey_str, false).expect("Should not have errored."); 161 let login_key_result = perform_key_retrieval(pubkey_str); 162 163 promise_assert!( 164 assert_eq, 165 Ok(Keypair::only_pubkey(expected_pubkey)), 166 &login_key_result 167 ); 168 } 169 170 #[test] 171 fn test_hex_pubkey() { 172 let pubkey_str = "32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245"; 173 let expected_pubkey = Pubkey::from_hex(pubkey_str).expect("Should not have errored."); 174 let login_key_result = perform_key_retrieval(pubkey_str); 175 176 promise_assert!( 177 assert_eq, 178 Ok(Keypair::only_pubkey(expected_pubkey)), 179 &login_key_result 180 ); 181 } 182 183 #[test] 184 fn test_privkey() { 185 let privkey_str = "nsec1g8wt3hlwjpa4827xylr3r0lccufxltyekhraexes8lqmpp2hensq5aujhs"; 186 let expected_privkey = SecretKey::from_str(privkey_str).expect("Should not have errored."); 187 let login_key_result = perform_key_retrieval(privkey_str); 188 189 promise_assert!( 190 assert_eq, 191 Ok(Keypair::from_secret(expected_privkey)), 192 &login_key_result 193 ); 194 } 195 196 #[test] 197 fn test_hex_privkey() { 198 let privkey_str = "41dcb8dfee907b53abc627c711bff8c7126fac99b5c7dc9b303fc1b08557cce0"; 199 let expected_privkey = SecretKey::from_str(privkey_str).expect("Should not have errored."); 200 let login_key_result = perform_key_retrieval(privkey_str); 201 202 promise_assert!( 203 assert_eq, 204 Ok(Keypair::from_secret(expected_privkey)), 205 &login_key_result 206 ); 207 } 208 209 #[test] 210 fn test_nip05() { 211 let nip05_str = "damus@damus.io"; 212 let expected_pubkey = Pubkey::try_from_bech32_string( 213 "npub18m76awca3y37hkvuneavuw6pjj4525fw90necxmadrvjg0sdy6qsngq955", 214 false, 215 ) 216 .expect("Should not have errored."); 217 let login_key_result = perform_key_retrieval(nip05_str); 218 219 promise_assert!( 220 assert_eq, 221 Ok(Keypair::only_pubkey(expected_pubkey)), 222 &login_key_result 223 ); 224 } 225 226 #[test] 227 fn test_nip05_pubkey() { 228 let nip05_str = "damus@damus.io"; 229 let expected_pubkey = Pubkey::try_from_bech32_string( 230 "npub18m76awca3y37hkvuneavuw6pjj4525fw90necxmadrvjg0sdy6qsngq955", 231 false, 232 ) 233 .expect("Should not have errored."); 234 let login_key_result = get_nip05_pubkey(nip05_str); 235 236 let res = login_key_result.block_and_take().expect("Should not error"); 237 assert_eq!(expected_pubkey, res); 238 } 239 }