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