1use crate::{
2 client, ContentEncryptionAlgorithm, EncryptedPrivateKey, Error, Event, EventKind, Filter,
3 KeySecurity, KeySigner, LockableSigner, PreEvent, PublicKey, RelayUrl, Signer,
4};
5use async_trait::async_trait;
6use serde::{Deserialize, Serialize};
7use std::sync::Arc;
8use std::time::Duration;
9
10mod request;
11pub use request::Nip46Request;
12
13mod response;
14pub use response::Nip46Response;
15
16mod params;
17pub use params::Nip46ConnectionParameters;
18
19mod prebunk;
20pub use prebunk::PreBunkerClient;
21
22#[derive(Debug, Serialize)]
24pub struct BunkerClient {
25 pub remote_signer_pubkey: PublicKey,
27
28 pub relay_url: RelayUrl,
30
31 pub local_signer: Arc<KeySigner>,
33
34 pub public_key: PublicKey,
36
37 pub timeout: Duration,
39
40 #[serde(skip_serializing)]
42 pub client: client::Client,
43}
44
45impl BunkerClient {
46 pub async fn from_stored_data(
48 remote_signer_pubkey: PublicKey,
49 relay_url: RelayUrl,
50 keysigner: KeySigner,
51 public_key: PublicKey,
52 timeout: Duration,
53 ) -> BunkerClient {
54 let client = client::Client::new(relay_url.as_str());
55 BunkerClient {
56 remote_signer_pubkey,
57 relay_url,
58 local_signer: Arc::new(keysigner),
59 public_key,
60 timeout,
61 client,
62 }
63 }
64
65 pub fn is_locked(&self) -> bool {
67 self.local_signer.is_locked()
68 }
69
70 pub fn unlock(&self, password: &str) -> Result<(), Error> {
72 self.local_signer.unlock(password)
73 }
74
75 pub fn lock(&self) {
77 self.local_signer.lock()
78 }
79
80 pub fn change_passphrase(&self, old: &str, new: &str, log_n: u8) -> Result<(), Error> {
82 self.local_signer.change_passphrase(old, new, log_n)
83 }
84
85 pub async fn call(&self, request: Nip46Request) -> Result<Nip46Response, Error> {
87 let mut filter = Filter::new();
89 filter.add_author(self.remote_signer_pubkey);
90 filter.add_event_kind(EventKind::NostrConnect);
91 filter.add_tag_value('p', self.local_signer.public_key().as_hex_string());
92 filter.limit = Some(1);
93 let sub_id = self.client.subscribe(filter.clone(), self.timeout).await?;
94 let event = request
95 .to_event(self.remote_signer_pubkey, self.local_signer.clone())
96 .await?;
97
98 let event_id = event.id;
100 self.client.post_event(event, self.timeout).await?;
101 let (ok, msg) = self.client.wait_for_ok(event_id, self.timeout).await?;
102 if !ok {
103 return Err(Error::Nip46FailedToPost(msg));
104 }
105
106 let event = self
108 .client
109 .wait_for_subscribed_event(sub_id.clone(), self.timeout)
110 .await?;
111
112 let contents = self.local_signer.decrypt_event_contents(&event).await?;
113
114 let response: Nip46Response = serde_json::from_str(&contents)?;
116
117 self.client.close_subscription(sub_id).await?;
119
120 Ok(response)
121 }
122
123 pub async fn disconnect(&self) -> Result<(), Error> {
125 self.client.disconnect().await
126 }
127
128 pub async fn disconnect_and_lock(&self) -> Result<(), Error> {
130 self.client.disconnect().await?;
131 self.local_signer.lock();
132 Ok(())
133 }
134}
135
136#[async_trait]
137impl Signer for BunkerClient {
138 fn public_key(&self) -> PublicKey {
139 self.public_key
140 }
141
142 fn encrypted_private_key(&self) -> Option<EncryptedPrivateKey> {
143 None
145 }
146
147 async fn sign_event(&self, pre_event: PreEvent) -> Result<Event, Error> {
148 if self.is_locked() {
149 return Err(Error::SignerIsLocked);
150 }
151
152 let pre_event_string = serde_json::to_string(&pre_event)?;
153 let request = Nip46Request::new("sign_event".to_owned(), vec![pre_event_string]);
154 let response = self.call(request).await?;
155 if let Some(error) = response.error {
156 if !error.is_empty() {
157 return Err(Error::Nip46Error(error));
158 }
159 }
160 let event: Event = serde_json::from_str(&response.result)?;
161 Ok(event)
162 }
163
164 async fn encrypt(
165 &self,
166 other: &PublicKey,
167 plaintext: &str,
168 algo: ContentEncryptionAlgorithm,
169 ) -> Result<String, Error> {
170 if self.is_locked() {
171 return Err(Error::SignerIsLocked);
172 }
173
174 let cmd = match algo {
175 ContentEncryptionAlgorithm::Nip04 => "nip04_encrypt",
176 ContentEncryptionAlgorithm::Nip44v1Unpadded => return Err(Error::UnsupportedAlgorithm),
177 ContentEncryptionAlgorithm::Nip44v1Padded => return Err(Error::UnsupportedAlgorithm),
178 ContentEncryptionAlgorithm::Nip44v2 => "nip44_encrypt",
179 };
180
181 let request = Nip46Request::new(
182 cmd.to_owned(),
183 vec![other.as_hex_string(), plaintext.to_owned()],
184 );
185
186 let response = self.call(request).await?;
187 if let Some(error) = response.error {
188 if !error.is_empty() {
189 return Err(Error::Nip46Error(error));
190 }
191 }
192
193 let ciphertext: String = serde_json::from_str(&response.result)?;
194
195 Ok(ciphertext)
196 }
197
198 async fn decrypt(&self, other: &PublicKey, ciphertext: &str) -> Result<String, Error> {
199 if self.is_locked() {
200 return Err(Error::SignerIsLocked);
201 }
202
203 let cmd = if ciphertext.contains("?iv=") {
204 "nip04_decrypt"
205 } else {
206 "nip44_decrypt"
207 };
208
209 let request = Nip46Request::new(
210 cmd.to_owned(),
211 vec![other.as_hex_string(), ciphertext.to_owned()],
212 );
213
214 let response = self.call(request).await?;
215 if let Some(error) = response.error {
216 if !error.is_empty() {
217 return Err(Error::Nip46Error(error));
218 }
219 }
220
221 let plaintext: String = serde_json::from_str(&response.result)?;
222
223 Ok(plaintext)
224 }
225
226 fn key_security(&self) -> Result<KeySecurity, Error> {
227 Ok(KeySecurity::NotTracked)
228 }
229}
230
231use serde::de::Error as DeError;
232use serde::de::{Deserializer, SeqAccess, Visitor};
233use std::fmt;
234
235impl<'de> Deserialize<'de> for BunkerClient {
236 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
237 where
238 D: Deserializer<'de>,
239 {
240 deserializer.deserialize_seq(BunkerClientVisitor)
241 }
242}
243
244struct BunkerClientVisitor;
245
246impl<'de> Visitor<'de> for BunkerClientVisitor {
247 type Value = BunkerClient;
248
249 fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
250 write!(f, "a serialized BunkerClient as a sequence")
251 }
252
253 fn visit_seq<A>(self, mut access: A) -> Result<BunkerClient, A::Error>
254 where
255 A: SeqAccess<'de>,
256 {
257 let remote_signer_pubkey = access
258 .next_element::<PublicKey>()?
259 .ok_or_else(|| DeError::custom("Missing remote_signer_pubkey"))?;
260 let relay_url = access
261 .next_element::<RelayUrl>()?
262 .ok_or_else(|| DeError::custom("Missing relay_url"))?;
263 let local_signer = access
264 .next_element::<Arc<KeySigner>>()?
265 .ok_or_else(|| DeError::custom("Missing local_signer"))?;
266 let public_key = access
267 .next_element::<PublicKey>()?
268 .ok_or_else(|| DeError::custom("Missing public_key"))?;
269 let timeout = access
270 .next_element::<Duration>()?
271 .ok_or_else(|| DeError::custom("Missing timeout"))?;
272 let client = client::Client::new(relay_url.as_str());
273 Ok(BunkerClient {
274 remote_signer_pubkey,
275 relay_url,
276 local_signer,
277 public_key,
278 timeout,
279 client,
280 })
281 }
282}
283
284#[cfg(test)]
285mod test {
286 use super::*;
287 use crate::PrivateKey;
288
289 #[test]
290 fn test_bunker_client_serde() {
291 let prebunk = PreBunkerClient::new(
292 PrivateKey::generate().public_key(),
293 RelayUrl::try_from_str("wss://relay.example/").unwrap(),
294 None,
295 "password",
296 )
297 .unwrap();
298
299 let s = serde_json::to_string(&prebunk).unwrap();
300 println!("{s}");
301 let prebunk2: PreBunkerClient = serde_json::from_str(&*s).unwrap();
302 assert_eq!(prebunk.remote_signer_pubkey, prebunk2.remote_signer_pubkey);
303 assert_eq!(prebunk.relay_url, prebunk2.relay_url);
304 assert_eq!(prebunk.connect_secret, prebunk2.connect_secret);
305 assert_eq!(
306 prebunk.local_signer.encrypted_private_key(),
307 prebunk2.local_signer.encrypted_private_key()
308 );
309 }
310}