chore: some fixes for nip4e

This commit is contained in:
2025-03-10 13:25:58 +07:00
parent ba0b377cee
commit 73b8a1a6da
2 changed files with 75 additions and 73 deletions

View File

@@ -1,4 +1,4 @@
use std::{sync::Arc, time::Duration};
use std::{str::FromStr, sync::Arc, time::Duration};
use anyhow::{anyhow, Error};
use common::profile::NostrProfile;
@@ -39,19 +39,32 @@ pub enum DeviceState {
impl DeviceState {
pub fn subscribe(&self, window: &mut Window, cx: &mut Context<Self>) {
log::info!("Device State: {:?}", self);
match self {
Self::Master => {
let client = get_client();
let task: Task<Result<(), Error>> = cx.background_spawn(async move {
let signer = client.signer().await?;
let public_key = signer.get_public_key().await?;
let opts =
SubscribeAutoCloseOptions::default().exit_policy(ReqExitPolicy::ExitOnEOSE);
let filter = Filter::new()
.kind(Kind::Custom(DEVICE_REQUEST_KIND))
.author(public_key)
.limit(1);
// Subscribe for the latest request
client.subscribe(filter, Some(opts)).await?;
let filter = Filter::new()
.kind(Kind::Custom(DEVICE_REQUEST_KIND))
.author(public_key)
.since(Timestamp::now());
// Subscribe for new device requests
_ = client.subscribe(filter, None).await;
client.subscribe(filter, None).await?;
Ok(())
});
@@ -110,6 +123,7 @@ pub struct Device {
client_keys: Arc<Keys>,
/// Device State
state: Entity<DeviceState>,
is_processing: bool,
}
pub fn init(window: &mut Window, cx: &App) {
@@ -162,6 +176,7 @@ pub fn init(window: &mut Window, cx: &App) {
let entity = cx.new(|_| Device {
profile: None,
is_processing: false,
state,
client_keys,
});
@@ -216,6 +231,11 @@ impl Device {
});
}
pub fn set_processing(&mut self, is_processing: bool, cx: &mut Context<Self>) {
self.is_processing = is_processing;
cx.notify();
}
/// Login and set user signer
pub fn login<T>(&self, signer: T, cx: &mut Context<Self>) -> Task<Result<(), Error>>
where
@@ -395,14 +415,22 @@ impl Device {
.detach();
}
/// Setup device
/// Setup Device
///
/// NIP-4e: <https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md>
pub fn setup_device(&mut self, window: &mut Window, cx: &Context<Self>) {
let Some(profile) = self.profile() else {
pub fn setup_device(&mut self, window: &mut Window, cx: &mut Context<Self>) {
let Some(profile) = self.profile().cloned() else {
return;
};
// If processing, return early
if self.is_processing {
return;
}
// Only process if device keys are not set
self.set_processing(true, cx);
let client = get_client();
let public_key = profile.public_key;
let kind = Kind::Custom(DEVICE_ANNOUNCEMENT_KIND);
@@ -442,8 +470,7 @@ impl Device {
cx.update(|window, cx| {
this.update(cx, |this, cx| {
this.request_keys(window, cx);
this.set_state(DeviceState::Master, cx);
this.request_master_keys(window, cx);
})
})
.ok();
@@ -457,7 +484,6 @@ impl Device {
cx.update(|window, cx| {
this.update(cx, |this, cx| {
this.set_master_keys(secret, window, cx);
this.set_state(DeviceState::Master, cx);
})
})
.ok();
@@ -468,8 +494,7 @@ impl Device {
cx.update(|window, cx| {
this.update(cx, |this, cx| {
this.request_keys(window, cx);
this.set_state(DeviceState::Minion, cx);
this.request_master_keys(window, cx);
})
})
.ok();
@@ -478,12 +503,11 @@ impl Device {
}
Err(_) => {
log::info!("Device Announcement not found.");
log::info!("Appoint this device as master.");
log::info!("Appoint this device as Master Device.");
cx.update(|window, cx| {
this.update(cx, |this, cx| {
this.set_new_master_keys(window, cx);
this.set_state(DeviceState::Master, cx);
})
.ok();
})
@@ -518,7 +542,7 @@ impl Device {
Ok(Arc::new(keys))
});
cx.spawn_in(window, |_, mut cx| async move {
cx.spawn_in(window, |this, mut cx| async move {
if get_device_keys().await.is_some() {
return;
}
@@ -539,6 +563,14 @@ impl Device {
log::error!("Failed to write device keys to keyring: {}", e);
}
};
cx.update(|_, cx| {
this.update(cx, |this, cx| {
this.set_state(DeviceState::Master, cx);
})
.ok();
})
.ok();
}
})
.detach();
@@ -558,9 +590,9 @@ impl Device {
log::info!("Re-appointing this device as Master Device.");
set_device_keys(keys).await;
cx.update(|window, cx| {
cx.update(|_, cx| {
this.update(cx, |this, cx| {
this.fetch_request(window, cx);
this.set_state(DeviceState::Master, cx);
})
.ok();
})
@@ -572,7 +604,7 @@ impl Device {
/// Send a request to ask for device keys from the other Nostr client
///
/// NIP-4e: <https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md>
pub fn request_keys(&self, window: &mut Window, cx: &Context<Self>) {
pub fn request_master_keys(&self, window: &mut Window, cx: &Context<Self>) {
let client = get_client();
let app_name = get_app_name();
let client_keys = self.client_keys.clone();
@@ -603,6 +635,7 @@ impl Device {
if task.await.is_ok() {
cx.update(|window, cx| {
this.update(cx, |this, cx| {
this.set_state(DeviceState::Minion, cx);
this.render_waiting_modal(window, cx);
})
.ok();
@@ -613,63 +646,31 @@ impl Device {
.detach();
}
/// Fetch the latest request from the other Nostr client
///
/// NIP-4e: <https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md>
pub fn fetch_request(&self, window: &mut Window, cx: &Context<Self>) {
let Some(profile) = self.profile() else {
return;
};
let client = get_client();
let public_key = profile.public_key;
let filter = Filter::new()
.kind(Kind::Custom(DEVICE_REQUEST_KIND))
.author(public_key)
.limit(1);
let task: Task<Result<Event, Error>> = cx.background_spawn(async move {
let events = client.fetch_events(filter, Duration::from_secs(2)).await?;
if let Some(event) = events.first_owned() {
Ok(event)
} else {
Err(anyhow!("No request found"))
}
});
cx.spawn_in(window, |this, mut cx| async move {
if let Ok(event) = task.await {
cx.update(|window, cx| {
this.update(cx, |this, cx| {
this.handle_request(event, window, cx);
})
.ok();
})
.ok();
}
})
.detach();
}
/// Received Device Keys approval from Master Device,
///
/// NIP-4e: <https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md>
pub fn handle_approval(&self, event: Event, window: &mut Window, cx: &Context<Self>) {
pub fn recv_approval(&self, event: Event, window: &mut Window, cx: &Context<Self>) {
let local_signer = self.client_keys.clone();
let task = cx.background_spawn(async move {
if let Some(public_key) = event.tags.public_keys().copied().last() {
let secret = local_signer
.nip44_decrypt(&public_key, &event.content)
.await?;
if let Some(tag) = event
.tags
.find(TagKind::custom("P"))
.and_then(|tag| tag.content())
{
if let Ok(public_key) = PublicKey::from_str(tag) {
let secret = local_signer
.nip44_decrypt(&public_key, &event.content)
.await?;
let keys = Arc::new(Keys::parse(&secret)?);
// Update global state with new device keys
set_device_keys(keys).await;
let keys = Arc::new(Keys::parse(&secret)?);
// Update global state with new device keys
set_device_keys(keys).await;
Ok(())
Ok(())
} else {
Err(anyhow!("Public Key is invalid"))
}
} else {
Err(anyhow!("Failed to decrypt the Master Keys"))
}
@@ -698,14 +699,14 @@ impl Device {
/// Received Master Keys request from other Nostr client
///
/// NIP-4e: <https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md>
pub fn handle_request(&self, event: Event, window: &mut Window, cx: &mut Context<Self>) {
let Some(public_key) = event
pub fn recv_request(&self, event: Event, window: &mut Window, cx: &mut Context<Self>) {
let Some(target_pubkey) = event
.tags
.find(TagKind::custom("pubkey"))
.and_then(|tag| tag.content())
.and_then(|content| PublicKey::parse(content).ok())
else {
log::error!("Invalid public key");
log::error!("Invalid public key.");
return;
};
@@ -742,14 +743,15 @@ impl Device {
// Get device's secret key
let device_secret = SecretKey::from_slice(&secret)?;
let device_secret_hex = device_secret.to_secret_hex();
// Encrypt device's secret key by using NIP-44
let content = local_signer
.nip44_encrypt(&public_key, &device_secret.to_secret_hex())
.nip44_encrypt(&target_pubkey, &device_secret_hex)
.await?;
// Create pubkey tag for other device (lowercase p)
let other_tag = Tag::public_key(public_key);
let other_tag = Tag::public_key(target_pubkey);
// Create pubkey tag for this device (uppercase P)
let local_tag = Tag::custom(

View File

@@ -178,7 +178,7 @@ fn main() {
.await;
}
Kind::Custom(DEVICE_ANNOUNCEMENT_KIND) => {
log::info!("Device announcement received");
log::info!("Device Announcement received");
if let Some(tag) = event
.tags
@@ -282,14 +282,14 @@ fn main() {
Signal::ReceiveMasterKey(event) => {
if let Some(device) = Device::global(cx) {
device.update(cx, |this, cx| {
this.handle_approval(event, window, cx);
this.recv_approval(event, window, cx);
});
}
}
Signal::RequestMasterKey(event) => {
if let Some(device) = Device::global(cx) {
device.update(cx, |this, cx| {
this.handle_request(event, window, cx);
this.recv_request(event, window, cx);
});
}
}