Public key import (and delete) [if you don't have a private key setup]

This commit is contained in:
Mike Dilger 2023-01-06 02:29:13 +13:00
parent 6b7be372da
commit 70a78551aa
6 changed files with 119 additions and 40 deletions

View File

@ -408,7 +408,11 @@ impl Overlord {
}
"generate_private_key" => {
let mut password: String = serde_json::from_str(&bus_message.json_payload)?;
GLOBALS.signer.write().await.generate_private_key(&password)?;
GLOBALS
.signer
.write()
.await
.generate_private_key(&password)?;
password.zeroize();
GLOBALS.signer.read().await.save_through_settings().await?;
}
@ -417,7 +421,11 @@ impl Overlord {
serde_json::from_str(&bus_message.json_payload)?;
let pk = PrivateKey::try_from_bech32_string(&import_bech32)?;
import_bech32.zeroize();
GLOBALS.signer.write().await.set_private_key(pk, &password)?;
GLOBALS
.signer
.write()
.await
.set_private_key(pk, &password)?;
password.zeroize();
GLOBALS.signer.read().await.save_through_settings().await?;
}
@ -426,10 +434,31 @@ impl Overlord {
serde_json::from_str(&bus_message.json_payload)?;
let pk = PrivateKey::try_from_hex_string(&import_hex)?;
import_hex.zeroize();
GLOBALS.signer.write().await.set_private_key(pk, &password)?;
GLOBALS
.signer
.write()
.await
.set_private_key(pk, &password)?;
password.zeroize();
GLOBALS.signer.read().await.save_through_settings().await?;
}
"import_pub" => {
let pubstr: String = serde_json::from_str(&bus_message.json_payload)?;
let maybe_pk1 = PublicKey::try_from_bech32_string(&pubstr);
let maybe_pk2 = PublicKey::try_from_hex_string(&pubstr);
if maybe_pk1.is_err() && maybe_pk2.is_err() {
*GLOBALS.status_message.write().await =
"Public key not recognized.".to_owned();
} else {
let pubkey = maybe_pk1.unwrap_or_else(|_| maybe_pk2.unwrap());
GLOBALS.signer.write().await.set_public_key(pubkey);
GLOBALS.signer.read().await.save_through_settings().await?;
}
}
"delete_pub" => {
GLOBALS.signer.write().await.clear_public_key();
GLOBALS.signer.read().await.save_through_settings().await?;
}
"save_relays" => {
let dirty_relays: Vec<DbRelay> = GLOBALS
.relays

View File

@ -3,35 +3,26 @@ use crate::globals::GLOBALS;
use nostr_types::{EncryptedPrivateKey, Event, KeySecurity, PreEvent, PrivateKey, PublicKey};
use tokio::task;
#[derive(Default)]
pub struct Signer {
public: Option<PublicKey>,
encrypted: Option<EncryptedPrivateKey>,
private: Option<PrivateKey>,
}
impl Default for Signer {
fn default() -> Signer {
Signer {
public: None,
encrypted: None,
private: None,
}
}
}
impl Signer {
pub async fn load_from_settings(&mut self) {
let settings = GLOBALS.settings.read().await;
*self = Signer {
public: settings.public_key,
encrypted: settings.encrypted_private_key.clone(),
private: None
private: None,
};
}
pub async fn save_through_settings(&self) -> Result<(), Error> {
let mut settings = GLOBALS.settings.write().await;
settings.public_key = self.public.clone();
settings.public_key = self.public;
settings.encrypted_private_key = self.encrypted.clone();
settings.save().await
}
@ -39,12 +30,23 @@ impl Signer {
#[allow(dead_code)]
pub fn set_public_key(&mut self, pk: PublicKey) {
if self.private.is_some() {
*GLOBALS.status_message.blocking_write() = "Ignored setting of public key (private key supercedes)".to_string();
*GLOBALS.status_message.blocking_write() =
"Ignored setting of public key (private key supercedes)".to_string();
} else {
self.public = Some(pk);
}
}
#[allow(dead_code)]
pub fn clear_public_key(&mut self) {
if self.private.is_some() {
*GLOBALS.status_message.blocking_write() =
"Ignored clearing of public key (private key supercedes)".to_string();
} else {
self.public = None;
}
}
#[allow(dead_code)]
pub fn set_encrypted_private_key(&mut self, epk: EncryptedPrivateKey) {
if self.private.is_some() && self.encrypted.is_some() {
@ -55,7 +57,7 @@ impl Signer {
}
pub fn set_private_key(&mut self, pk: PrivateKey, pass: &str) -> Result<(), Error> {
self.encrypted = Some(pk.export_encrypted(&pass)?);
self.encrypted = Some(pk.export_encrypted(pass)?);
self.public = Some(pk.public_key());
self.private = Some(pk);
Ok(())
@ -65,8 +67,7 @@ impl Signer {
if self.private.is_some() {
// ignore, already unlocked
Ok(())
}
else if let Some(epk) = &self.encrypted {
} else if let Some(epk) = &self.encrypted {
self.private = Some(epk.decrypt(pass)?);
Ok(())
} else {
@ -91,7 +92,7 @@ impl Signer {
}
pub fn public_key(&self) -> Option<PublicKey> {
self.public.clone()
self.public
}
pub fn encrypted_private_key(&self) -> Option<EncryptedPrivateKey> {
@ -99,18 +100,14 @@ impl Signer {
}
pub fn key_security(&self) -> Option<KeySecurity> {
if let Some(pk) = &self.private {
Some(pk.key_security())
} else {
None
}
self.private.as_ref().map(|pk| pk.key_security())
}
pub fn sign_preevent(&self, preevent: PreEvent, pow: Option<u8>) -> Result<Event, Error> {
match &self.private {
Some(pk) => match pow {
Some(pow) => Ok(Event::new_with_pow(preevent, &pk, pow)?),
None => Ok(Event::new(preevent, &pk)?),
Some(pow) => Ok(Event::new_with_pow(preevent, pk, pow)?),
None => Ok(Event::new(preevent, pk)?),
},
_ => Err(Error::NoPrivateKey),
}

View File

@ -72,11 +72,11 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, frame: &mut eframe::Fram
Err(_) => -1,
};
/*
let incoming_count: isize = match GLOBALS.incoming_events.try_read() {
Ok(v) => v.len() as isize,
Err(_) => -1,
};
*/
let incoming_count: isize = match GLOBALS.incoming_events.try_read() {
Ok(v) => v.len() as isize,
Err(_) => -1,
};
*/
ui.with_layout(Layout::right_to_left(Align::TOP), |ui| {
if ui
@ -586,7 +586,8 @@ fn render_content(app: &mut GossipUi, ui: &mut Ui, tag_re: &regex::Regex, event:
}
Tag::Hashtag(s) => {
if ui.link(format!("#{}", s)).clicked() {
*GLOBALS.status_message.blocking_write() = "Gossip doesn't have a hashtag feed yet.".to_owned();
*GLOBALS.status_message.blocking_write() =
"Gossip doesn't have a hashtag feed yet.".to_owned();
}
}
_ => {

View File

@ -84,6 +84,7 @@ struct GossipUi {
password: String,
import_bech32: String,
import_hex: String,
import_pub: String,
replying_to: Option<Id>,
person_view_pubkey: Option<PublicKeyHex>,
avatars: HashMap<PublicKeyHex, TextureHandle>,
@ -154,6 +155,7 @@ impl GossipUi {
password: "".to_owned(),
import_bech32: "".to_owned(),
import_hex: "".to_owned(),
import_pub: "".to_owned(),
replying_to: None,
person_view_pubkey: None,
avatars: HashMap::new(),
@ -253,7 +255,10 @@ impl eframe::App for GossipUi {
egui::TopBottomPanel::bottom("status").show(ctx, |ui| {
ui.horizontal(|ui| {
if ui
.add(Label::new(GLOBALS.status_message.blocking_read().clone()).sense(Sense::click()))
.add(
Label::new(GLOBALS.status_message.blocking_read().clone())
.sense(Sense::click()),
)
.clicked()
{
*GLOBALS.status_message.blocking_write() = "".to_string();

View File

@ -36,7 +36,8 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
&app.new_relay_url
);
} else {
*GLOBALS.status_message.blocking_write() = "That's not a valid relay URL.".to_owned();
*GLOBALS.status_message.blocking_write() =
"That's not a valid relay URL.".to_owned();
}
}
});

View File

@ -86,8 +86,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
*GLOBALS.status_message.blocking_write() =
"Exported key has been printed to the console standard output.".to_owned();
}
Err(e) =>
*GLOBALS.status_message.blocking_write() = format!("{}", e),
Err(e) => *GLOBALS.status_message.blocking_write() = format!("{}", e),
}
app.password.zeroize();
app.password = "".to_owned();
@ -200,9 +199,7 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
app.password = "".to_owned();
}
ui.add_space(10.0);
ui.separator();
ui.add_space(10.0);
ui.add_space(20.0);
ui.heading("Import a hex private key");
@ -230,5 +227,54 @@ pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Fr
app.password.zeroize();
app.password = "".to_owned();
}
ui.add_space(10.0);
ui.separator();
ui.add_space(10.0);
ui.heading("Public Key");
ui.add_space(10.0);
ui.label("You can just import your public key if you only want to view events and don't want to use gossip to create events. This will allow you to (eventually) sync your follow list and follow people on gossip without copying your private key here.");
if let Some(pk) = GLOBALS.signer.blocking_read().public_key() {
let pkhex: PublicKeyHex = pk.into();
ui.horizontal(|ui| {
ui.label(&format!("Public Key (Hex): {}", pkhex.0));
if ui.add(CopyButton {}).clicked() {
ui.output().copied_text = pkhex.0;
}
});
if let Ok(bech32) = pk.try_as_bech32_string() {
ui.horizontal(|ui| {
ui.label(&format!("Public Key (bech32): {}", bech32));
if ui.add(CopyButton {}).clicked() {
ui.output().copied_text = bech32;
}
});
}
if ui.button("Delete this public key").clicked() {
let _ = GLOBALS.to_overlord.send(BusMessage {
target: "overlord".to_string(),
kind: "delete_pub".to_string(),
json_payload: serde_json::to_string(&app.import_pub).unwrap(),
});
}
} else {
ui.horizontal_wrapped(|ui| {
ui.label("Enter your public key");
ui.add(TextEdit::singleline(&mut app.import_pub).hint_text("npub1 or hex"));
if ui.button("Import a Public Key").clicked() {
let _ = GLOBALS.to_overlord.send(BusMessage {
target: "overlord".to_string(),
kind: "import_pub".to_string(),
json_payload: serde_json::to_string(&app.import_pub).unwrap(),
});
app.import_pub = "".to_owned();
}
});
}
}
}