wip: dekey
This commit is contained in:
@@ -39,7 +39,6 @@ use crate::text::RenderedText;
|
||||
|
||||
mod actions;
|
||||
mod emoji;
|
||||
mod subject;
|
||||
mod text;
|
||||
|
||||
pub fn init(room: WeakEntity<Room>, window: &mut Window, cx: &mut App) -> Entity<ChatPanel> {
|
||||
@@ -601,7 +600,6 @@ impl ChatPanel {
|
||||
text: AnyElement,
|
||||
cx: &Context<Self>,
|
||||
) -> AnyElement {
|
||||
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
||||
let hide_avatar = AppSettings::get_hide_user_avatars(cx);
|
||||
|
||||
let id = message.id;
|
||||
@@ -1132,7 +1130,6 @@ impl Panel for ChatPanel {
|
||||
fn title(&self, cx: &App) -> AnyElement {
|
||||
self.room
|
||||
.read_with(cx, |this, cx| {
|
||||
let proxy = AppSettings::get_proxy_user_avatars(cx);
|
||||
let label = this.display_name(cx);
|
||||
let url = this.display_image(cx);
|
||||
|
||||
|
||||
@@ -541,7 +541,7 @@ impl ChatSpace {
|
||||
}),
|
||||
)
|
||||
})
|
||||
.when_some(identity.read(cx).option_public_key(), |this, public_key| {
|
||||
.when_some(identity.read(cx).public_key, |this, public_key| {
|
||||
let persons = PersonRegistry::global(cx);
|
||||
let profile = persons.read(cx).get(&public_key, cx);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ use gpui::{App, AppContext, Context, Entity, Global, Task};
|
||||
use nostr_sdk::prelude::*;
|
||||
pub use person::*;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use state::{NostrRegistry, TIMEOUT};
|
||||
use state::{Announcement, NostrRegistry, TIMEOUT};
|
||||
|
||||
mod person;
|
||||
|
||||
@@ -21,6 +21,12 @@ struct GlobalPersonRegistry(Entity<PersonRegistry>);
|
||||
|
||||
impl Global for GlobalPersonRegistry {}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Dispatch {
|
||||
Person(Box<Person>),
|
||||
Announcement(Box<Event>),
|
||||
}
|
||||
|
||||
/// Person Registry
|
||||
#[derive(Debug)]
|
||||
pub struct PersonRegistry {
|
||||
@@ -54,7 +60,7 @@ impl PersonRegistry {
|
||||
let client = nostr.read(cx).client();
|
||||
|
||||
// Channel for communication between nostr and gpui
|
||||
let (tx, rx) = flume::bounded::<Person>(100);
|
||||
let (tx, rx) = flume::bounded::<Dispatch>(100);
|
||||
let (mta_tx, mta_rx) = flume::bounded::<PublicKey>(100);
|
||||
|
||||
let mut tasks = smallvec![];
|
||||
@@ -84,9 +90,16 @@ impl PersonRegistry {
|
||||
tasks.push(
|
||||
// Update GPUI state
|
||||
cx.spawn(async move |this, cx| {
|
||||
while let Ok(person) = rx.recv_async().await {
|
||||
while let Ok(event) = rx.recv_async().await {
|
||||
this.update(cx, |this, cx| {
|
||||
this.insert(person, cx);
|
||||
match event {
|
||||
Dispatch::Person(person) => {
|
||||
this.insert(*person, cx);
|
||||
}
|
||||
Dispatch::Announcement(event) => {
|
||||
this.set_announcement(&event, cx);
|
||||
}
|
||||
};
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
@@ -124,7 +137,7 @@ impl PersonRegistry {
|
||||
}
|
||||
|
||||
/// Handle nostr notifications
|
||||
async fn handle_notifications(client: &Client, tx: &flume::Sender<Person>) {
|
||||
async fn handle_notifications(client: &Client, tx: &flume::Sender<Dispatch>) {
|
||||
let mut notifications = client.notifications();
|
||||
let mut processed_events = HashSet::new();
|
||||
|
||||
@@ -144,12 +157,21 @@ impl PersonRegistry {
|
||||
Kind::Metadata => {
|
||||
let metadata = Metadata::from_json(&event.content).unwrap_or_default();
|
||||
let person = Person::new(event.pubkey, metadata);
|
||||
let val = Box::new(person);
|
||||
|
||||
tx.send_async(person).await.ok();
|
||||
// Send
|
||||
tx.send_async(Dispatch::Person(val)).await.ok();
|
||||
}
|
||||
Kind::Custom(10044) => {
|
||||
let val = Box::new(event.into_owned());
|
||||
|
||||
// Send
|
||||
tx.send_async(Dispatch::Announcement(val)).await.ok();
|
||||
}
|
||||
Kind::ContactList => {
|
||||
let public_keys = event.extract_public_keys();
|
||||
|
||||
// Get metadata for all public keys
|
||||
Self::get_metadata(client, public_keys).await.ok();
|
||||
}
|
||||
_ => {}
|
||||
@@ -232,6 +254,18 @@ impl PersonRegistry {
|
||||
Ok(persons)
|
||||
}
|
||||
|
||||
/// Set profile encryption keys announcement
|
||||
fn set_announcement(&mut self, event: &Event, cx: &mut App) {
|
||||
if let Some(person) = self.persons.get(&event.pubkey) {
|
||||
let announcement = Announcement::from(event);
|
||||
|
||||
person.update(cx, |person, cx| {
|
||||
person.set_announcement(announcement);
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Insert batch of persons
|
||||
fn bulk_inserts(&mut self, persons: Vec<Person>, cx: &mut Context<Self>) {
|
||||
for person in persons.into_iter() {
|
||||
|
||||
@@ -8,8 +8,13 @@ use state::Announcement;
|
||||
/// Person
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Person {
|
||||
/// Public Key
|
||||
public_key: PublicKey,
|
||||
|
||||
/// Metadata (profile)
|
||||
metadata: Metadata,
|
||||
|
||||
/// Dekey (NIP-4e) announcement
|
||||
announcement: Option<Announcement>,
|
||||
}
|
||||
|
||||
@@ -69,6 +74,12 @@ impl Person {
|
||||
self.announcement.clone()
|
||||
}
|
||||
|
||||
/// Set profile encryption keys announcement
|
||||
pub fn set_announcement(&mut self, announcement: Announcement) {
|
||||
self.announcement = Some(announcement);
|
||||
log::info!("Updated announcement for: {}", self.public_key());
|
||||
}
|
||||
|
||||
/// Get profile avatar
|
||||
pub fn avatar(&self) -> SharedString {
|
||||
self.metadata()
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
use gpui::SharedString;
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub enum DeviceState {
|
||||
#[default]
|
||||
Initial,
|
||||
Requesting,
|
||||
Set,
|
||||
}
|
||||
|
||||
/// Announcement
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Announcement {
|
||||
id: EventId,
|
||||
/// The public key of the device that created this announcement.
|
||||
public_key: PublicKey,
|
||||
|
||||
/// The name of the device that created this announcement.
|
||||
client_name: Option<String>,
|
||||
}
|
||||
|
||||
@@ -24,27 +35,24 @@ impl From<&Event> for Announcement {
|
||||
.and_then(|tag| tag.content())
|
||||
.map(|c| c.to_string());
|
||||
|
||||
Self::new(val.id, client_name, public_key)
|
||||
Self::new(public_key, client_name)
|
||||
}
|
||||
}
|
||||
|
||||
impl Announcement {
|
||||
pub fn new(id: EventId, client_name: Option<String>, public_key: PublicKey) -> Self {
|
||||
pub fn new(public_key: PublicKey, client_name: Option<String>) -> Self {
|
||||
Self {
|
||||
id,
|
||||
client_name,
|
||||
public_key,
|
||||
client_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn id(&self) -> EventId {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Returns the public key of the device that created this announcement.
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
self.public_key
|
||||
}
|
||||
|
||||
/// Returns the client name of the device that created this announcement.
|
||||
pub fn client_name(&self) -> SharedString {
|
||||
self.client_name
|
||||
.as_ref()
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use nostr_sdk::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::Announcement;
|
||||
use nostr_sdk::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub enum RelayState {
|
||||
@@ -16,13 +16,15 @@ impl RelayState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Identity {
|
||||
/// The public key of the account
|
||||
public_key: Option<PublicKey>,
|
||||
pub public_key: Option<PublicKey>,
|
||||
|
||||
/// Encryption key announcement
|
||||
announcement: Option<Announcement>,
|
||||
/// Decoupled encryption key
|
||||
///
|
||||
/// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
|
||||
dekey: Option<Arc<dyn NostrSigner>>,
|
||||
|
||||
/// Status of the current user NIP-65 relays
|
||||
relay_list: RelayState,
|
||||
@@ -41,7 +43,7 @@ impl Identity {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
public_key: None,
|
||||
announcement: None,
|
||||
dekey: None,
|
||||
relay_list: RelayState::default(),
|
||||
messaging_relays: RelayState::default(),
|
||||
}
|
||||
@@ -57,6 +59,7 @@ impl Identity {
|
||||
self.relay_list
|
||||
}
|
||||
|
||||
/// Sets the state of the NIP-17 relays.
|
||||
pub fn set_messaging_relays_state(&mut self, state: RelayState) {
|
||||
self.messaging_relays = state;
|
||||
}
|
||||
@@ -66,6 +69,26 @@ impl Identity {
|
||||
self.messaging_relays
|
||||
}
|
||||
|
||||
/// Returns the decoupled encryption key.
|
||||
pub fn dekey(&self) -> Option<Arc<dyn NostrSigner>> {
|
||||
self.dekey.clone()
|
||||
}
|
||||
|
||||
/// Sets the decoupled encryption key.
|
||||
pub fn set_dekey<S>(&mut self, dekey: S)
|
||||
where
|
||||
S: NostrSigner + 'static,
|
||||
{
|
||||
self.dekey = Some(Arc::new(dekey));
|
||||
}
|
||||
|
||||
/// Force getting the public key of the identity.
|
||||
///
|
||||
/// Panics if the public key is not set.
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
self.public_key.unwrap()
|
||||
}
|
||||
|
||||
/// Returns true if the identity has a public key.
|
||||
pub fn has_public_key(&self) -> bool {
|
||||
self.public_key.is_some()
|
||||
@@ -80,15 +103,4 @@ impl Identity {
|
||||
pub fn unset_public_key(&mut self) {
|
||||
self.public_key = None;
|
||||
}
|
||||
|
||||
/// Returns the public key of the identity.
|
||||
pub fn option_public_key(&self) -> Option<PublicKey> {
|
||||
self.public_key
|
||||
}
|
||||
|
||||
/// Returns the public key of the identity.
|
||||
pub fn public_key(&self) -> PublicKey {
|
||||
// This method is safe to unwrap because the public key is always called when the identity is created.
|
||||
self.public_key.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use std::collections::HashSet;
|
||||
use std::time::Duration;
|
||||
|
||||
use anyhow::Error;
|
||||
use common::{config_dir, BOOTSTRAP_RELAYS, SEARCH_RELAYS};
|
||||
use anyhow::{anyhow, Context as AnyhowContext, Error};
|
||||
use common::{app_name, config_dir, BOOTSTRAP_RELAYS, SEARCH_RELAYS};
|
||||
use gpui::{App, AppContext, Context, Entity, Global, Subscription, Task};
|
||||
use nostr_lmdb::NostrLmdb;
|
||||
use nostr_sdk::prelude::*;
|
||||
@@ -52,6 +52,11 @@ pub struct NostrRegistry {
|
||||
/// Gossip implementation
|
||||
gossip: Entity<Gossip>,
|
||||
|
||||
/// Device state
|
||||
///
|
||||
/// NIP-4e: https://github.com/nostr-protocol/nips/blob/per-device-keys/4e.md
|
||||
device_state: Entity<DeviceState>,
|
||||
|
||||
/// Tasks for asynchronous operations
|
||||
tasks: Vec<Task<Result<(), Error>>>,
|
||||
|
||||
@@ -108,6 +113,7 @@ impl NostrRegistry {
|
||||
|
||||
// Construct the identity entity
|
||||
let identity = cx.new(|_| Identity::default());
|
||||
let device_state = cx.new(|_| DeviceState::default());
|
||||
|
||||
// Channel for communication between nostr and gpui
|
||||
let (tx, rx) = flume::bounded::<Event>(2048);
|
||||
@@ -123,16 +129,18 @@ impl NostrRegistry {
|
||||
RelayState::Initial => {
|
||||
this.get_relay_list(cx);
|
||||
}
|
||||
RelayState::Set => match state.read(cx).messaging_relays_state() {
|
||||
RelayState::Initial => {
|
||||
this.get_profile(cx);
|
||||
this.get_messaging_relays(cx);
|
||||
}
|
||||
RelayState::Set => {
|
||||
this.get_messages(cx);
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
RelayState::Set => {
|
||||
match state.read(cx).messaging_relays_state() {
|
||||
RelayState::Initial => {
|
||||
this.get_profile(cx);
|
||||
this.get_messaging_relays(cx);
|
||||
}
|
||||
RelayState::Set => {
|
||||
this.get_messages(state.read(cx).dekey(), cx);
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -150,7 +158,7 @@ impl NostrRegistry {
|
||||
|
||||
tasks.push(
|
||||
// Update GPUI states
|
||||
cx.spawn(async move |_this, cx| {
|
||||
cx.spawn(async move |this, cx| {
|
||||
while let Ok(event) = rx.recv_async().await {
|
||||
match event.kind {
|
||||
Kind::RelayList => {
|
||||
@@ -165,6 +173,11 @@ impl NostrRegistry {
|
||||
cx.notify();
|
||||
})?;
|
||||
}
|
||||
Kind::Custom(10044) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.init_dekey(&event, cx);
|
||||
})?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -176,6 +189,7 @@ impl NostrRegistry {
|
||||
Self {
|
||||
client,
|
||||
identity,
|
||||
device_state,
|
||||
gossip,
|
||||
app_keys,
|
||||
_subscriptions: subscriptions,
|
||||
@@ -183,7 +197,7 @@ impl NostrRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
// Handle nostr notifications
|
||||
/// Handle nostr notifications
|
||||
async fn handle_notifications(client: &Client, tx: &flume::Sender<Event>) -> Result<(), Error> {
|
||||
// Add bootstrap relay to the relay pool
|
||||
for url in BOOTSTRAP_RELAYS.into_iter() {
|
||||
@@ -198,6 +212,7 @@ impl NostrRegistry {
|
||||
// Connect to all added relays
|
||||
client.connect().await;
|
||||
|
||||
// Handle nostr notifications
|
||||
let mut notifications = client.notifications();
|
||||
let mut processed_events = HashSet::new();
|
||||
|
||||
@@ -217,7 +232,7 @@ impl NostrRegistry {
|
||||
Kind::RelayList => {
|
||||
// Automatically get messaging relays for each member when the user opens a room
|
||||
if subscription_id.as_str().starts_with("room-") {
|
||||
Self::get_messaging_relays_by(client, event.as_ref()).await?;
|
||||
Self::get_adv_events_by(client, event.as_ref()).await?;
|
||||
}
|
||||
|
||||
tx.send_async(event.into_owned()).await?;
|
||||
@@ -225,6 +240,16 @@ impl NostrRegistry {
|
||||
Kind::InboxRelays => {
|
||||
tx.send_async(event.into_owned()).await?;
|
||||
}
|
||||
Kind::Custom(10044) => {
|
||||
if let Ok(signer) = client.signer().await {
|
||||
if let Ok(public_key) = signer.get_public_key().await {
|
||||
// Only send if the event is from the current user
|
||||
if public_key == event.pubkey {
|
||||
tx.send_async(event.into_owned()).await?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@@ -251,8 +276,8 @@ impl NostrRegistry {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Automatically get messaging relays from a received relay list
|
||||
async fn get_messaging_relays_by(client: &Client, event: &Event) -> Result<(), Error> {
|
||||
/// Automatically get messaging relays and encryption announcement from a received relay list
|
||||
async fn get_adv_events_by(client: &Client, event: &Event) -> Result<(), Error> {
|
||||
// Subscription options
|
||||
let opts = SubscribeAutoCloseOptions::default()
|
||||
.timeout(Some(Duration::from_secs(TIMEOUT)))
|
||||
@@ -276,16 +301,20 @@ impl NostrRegistry {
|
||||
}
|
||||
|
||||
// Construct filter for inbox relays
|
||||
let filter = Filter::new()
|
||||
let inbox = Filter::new()
|
||||
.kind(Kind::InboxRelays)
|
||||
.author(event.pubkey)
|
||||
.limit(1);
|
||||
|
||||
client
|
||||
.subscribe_to(write_relays, vec![filter], Some(opts))
|
||||
.await?;
|
||||
// Construct filter for encryption announcement
|
||||
let announcement = Filter::new()
|
||||
.kind(Kind::Custom(10044))
|
||||
.author(event.pubkey)
|
||||
.limit(1);
|
||||
|
||||
log::info!("Getting inbox relays for: {}", event.pubkey);
|
||||
client
|
||||
.subscribe_to(write_relays, vec![inbox, announcement], Some(opts))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -528,18 +557,8 @@ impl NostrRegistry {
|
||||
.limit(1)
|
||||
.author(public_key);
|
||||
|
||||
// Filter for encryption keys announcement
|
||||
let encryption_keys = Filter::new()
|
||||
.kind(Kind::Custom(10044))
|
||||
.limit(1)
|
||||
.author(public_key);
|
||||
|
||||
client
|
||||
.subscribe_to(
|
||||
urls,
|
||||
vec![metadata, contact_list, encryption_keys],
|
||||
Some(opts),
|
||||
)
|
||||
.subscribe_to(urls, vec![metadata, contact_list], Some(opts))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
@@ -604,7 +623,10 @@ impl NostrRegistry {
|
||||
}
|
||||
|
||||
/// Continuously get gift wrap events for the current user in their messaging relays
|
||||
fn get_messages(&mut self, cx: &mut Context<Self>) {
|
||||
fn get_messages<T>(&mut self, dekey: Option<T>, cx: &mut Context<Self>)
|
||||
where
|
||||
T: NostrSigner + 'static,
|
||||
{
|
||||
let client = self.client();
|
||||
let public_key = self.identity().read(cx).public_key();
|
||||
let messaging_relays = self.messaging_relays(&public_key, cx);
|
||||
@@ -612,43 +634,176 @@ impl NostrRegistry {
|
||||
cx.background_spawn(async move {
|
||||
let urls = messaging_relays.await;
|
||||
let id = SubscriptionId::new(GIFTWRAP_SUBSCRIPTION);
|
||||
let filter = Filter::new().kind(Kind::GiftWrap).pubkey(public_key);
|
||||
let mut filters = vec![];
|
||||
|
||||
if let Err(e) = client
|
||||
.subscribe_with_id_to(urls, id, vec![filter], None)
|
||||
.await
|
||||
{
|
||||
// Construct a filter to get user messages
|
||||
filters.push(Filter::new().kind(Kind::GiftWrap).pubkey(public_key));
|
||||
|
||||
// Construct a filter to get dekey messages if available
|
||||
if let Some(signer) = dekey {
|
||||
if let Ok(pubkey) = signer.get_public_key().await {
|
||||
filters.push(Filter::new().kind(Kind::GiftWrap).pubkey(pubkey));
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(e) = client.subscribe_with_id_to(urls, id, filters, None).await {
|
||||
log::error!("Failed to subscribe to gift wrap events: {e}");
|
||||
}
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
/// Subscribe to event kinds to author's write relays
|
||||
pub fn subscribe<I>(&self, kinds: I, author: PublicKey, cx: &App)
|
||||
/// Set the decoupled encryption key for the current user
|
||||
fn set_dekey<T>(&mut self, dekey: T, cx: &mut Context<Self>)
|
||||
where
|
||||
I: Into<Vec<Kind>>,
|
||||
T: NostrSigner + 'static,
|
||||
{
|
||||
self.identity.update(cx, |this, cx| {
|
||||
this.set_dekey(dekey);
|
||||
cx.notify();
|
||||
});
|
||||
self.device_state.update(cx, |this, cx| {
|
||||
*this = DeviceState::Set;
|
||||
cx.notify();
|
||||
});
|
||||
}
|
||||
|
||||
/// Initialize dekey (decoupled encryption key) for the current user
|
||||
fn init_dekey(&mut self, event: &Event, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
let write_relays = self.write_relays(&author, cx);
|
||||
let announcement = Announcement::from(event);
|
||||
let dekey = announcement.public_key();
|
||||
|
||||
// Construct filters based on event kinds
|
||||
let filters: Vec<Filter> = kinds
|
||||
.into()
|
||||
.into_iter()
|
||||
.map(|kind| Filter::new().kind(kind).author(author).limit(1))
|
||||
.collect();
|
||||
let task: Task<Result<Keys, Error>> = cx.background_spawn(async move {
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
|
||||
cx.background_spawn(async move {
|
||||
let urls = write_relays.await;
|
||||
let filter = Filter::new()
|
||||
.identifier("coop:device")
|
||||
.kind(Kind::ApplicationSpecificData)
|
||||
.author(public_key)
|
||||
.limit(1);
|
||||
|
||||
// Construct subscription options
|
||||
let opts = SubscribeAutoCloseOptions::default()
|
||||
.timeout(Some(Duration::from_secs(TIMEOUT)))
|
||||
.exit_policy(ReqExitPolicy::ExitOnEOSE);
|
||||
if let Some(event) = client.database().query(filter).await?.first() {
|
||||
let content = signer.nip44_decrypt(&public_key, &event.content).await?;
|
||||
let secret = SecretKey::parse(&content)?;
|
||||
let keys = Keys::new(secret);
|
||||
|
||||
if let Err(e) = client.subscribe_to(urls, filters, Some(opts)).await {
|
||||
log::error!("Failed to create a subscription: {e}");
|
||||
if keys.public_key() == dekey {
|
||||
Ok(keys)
|
||||
} else {
|
||||
Err(anyhow!("Key mismatch"))
|
||||
}
|
||||
} else {
|
||||
Err(anyhow!("Key not found"))
|
||||
}
|
||||
});
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
match task.await {
|
||||
Ok(keys) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_dekey(keys, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
log::warn!("Failed to initialize dekey: {e}");
|
||||
this.update(cx, |this, cx| {
|
||||
this.request_dekey(cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
/// Request dekey from other device
|
||||
fn request_dekey(&mut self, cx: &mut Context<Self>) {
|
||||
let client = self.client();
|
||||
let device_state = self.device_state.downgrade();
|
||||
let public_key = self.identity().read(cx).public_key();
|
||||
let write_relays = self.write_relays(&public_key, cx);
|
||||
|
||||
let app_keys = self.app_keys().clone();
|
||||
let app_pubkey = app_keys.public_key();
|
||||
|
||||
let task: Task<Result<Option<Keys>, Error>> = cx.background_spawn(async move {
|
||||
let signer = client.signer().await?;
|
||||
let public_key = signer.get_public_key().await?;
|
||||
|
||||
let filter = Filter::new()
|
||||
.kind(Kind::Custom(4455))
|
||||
.author(public_key)
|
||||
.pubkey(app_pubkey)
|
||||
.limit(1);
|
||||
|
||||
match client.database().query(filter).await?.first_owned() {
|
||||
Some(event) => {
|
||||
let root_device = event
|
||||
.tags
|
||||
.find(TagKind::custom("P"))
|
||||
.and_then(|tag| tag.content())
|
||||
.and_then(|content| PublicKey::parse(content).ok())
|
||||
.context("Invalid event's tags")?;
|
||||
|
||||
let payload = event.content.as_str();
|
||||
let decrypted = app_keys.nip44_decrypt(&root_device, payload).await?;
|
||||
|
||||
let secret = SecretKey::from_hex(&decrypted)?;
|
||||
let keys = Keys::new(secret);
|
||||
|
||||
Ok(Some(keys))
|
||||
}
|
||||
None => {
|
||||
let urls = write_relays.await;
|
||||
|
||||
// Construct an event for device key request
|
||||
let event = EventBuilder::new(Kind::Custom(4454), "")
|
||||
.tags(vec![
|
||||
Tag::client(app_name()),
|
||||
Tag::custom(TagKind::custom("P"), vec![app_pubkey]),
|
||||
])
|
||||
.sign(&signer)
|
||||
.await?;
|
||||
|
||||
// Send the event to write relays
|
||||
client.send_event_to(&urls, &event).await?;
|
||||
|
||||
// Construct a filter to get the approval response event
|
||||
let filter = Filter::new()
|
||||
.kind(Kind::Custom(4455))
|
||||
.author(public_key)
|
||||
.since(Timestamp::now());
|
||||
|
||||
// Subscribe to the approval response event
|
||||
client.subscribe_to(&urls, vec![filter], None).await?;
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cx.spawn(async move |this, cx| {
|
||||
match task.await {
|
||||
Ok(Some(keys)) => {
|
||||
this.update(cx, |this, cx| {
|
||||
this.set_dekey(keys, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
Ok(None) => {
|
||||
device_state
|
||||
.update(cx, |this, cx| {
|
||||
*this = DeviceState::Requesting;
|
||||
cx.notify();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!("Failed to request the encryption key: {e}");
|
||||
}
|
||||
};
|
||||
})
|
||||
.detach();
|
||||
|
||||
Reference in New Issue
Block a user