From e327891a5257c586ad7ff82db3cdcc990db1350f Mon Sep 17 00:00:00 2001 From: Mike Dilger Date: Tue, 27 Dec 2022 21:21:22 +1300 Subject: [PATCH] Generate or Import a Key pair --- src/overlord/mod.rs | 44 ++++++++++++++++ src/ui/mod.rs | 13 +++++ src/ui/people.rs | 2 + src/ui/you.rs | 121 ++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 177 insertions(+), 3 deletions(-) diff --git a/src/overlord/mod.rs b/src/overlord/mod.rs index f3f8bb1e..9a3bfb08 100644 --- a/src/overlord/mod.rs +++ b/src/overlord/mod.rs @@ -14,6 +14,7 @@ use tokio::sync::broadcast::Sender; use tokio::sync::mpsc::UnboundedReceiver; use tokio::{select, task}; use tracing::{debug, error, info, warn}; +use zeroize::Zeroize; pub struct Overlord { to_minions: Sender, @@ -384,6 +385,49 @@ impl Overlord { let data: (String, String) = serde_json::from_str(&bus_message.json_payload)?; Overlord::follow_hexkey(data.0, data.1).await?; } + "unlock_key" => { + let mut password: String = serde_json::from_str(&bus_message.json_payload)?; + GLOBALS + .signer + .write() + .await + .unlock_encrypted_private_key(&password)?; + password.zeroize(); + + // Update public key from private key + let public_key = GLOBALS.signer.read().await.public_key().unwrap(); + { + let mut settings = GLOBALS.settings.write().await; + settings.public_key = Some(public_key); + settings.save().await?; + } + } + "import_bech32" => { + let (mut import_bech32, mut password): (String, String) = + serde_json::from_str(&bus_message.json_payload)?; + let pk = PrivateKey::try_from_bech32_string(&import_bech32)?; + import_bech32.zeroize(); + let epk = pk.export_encrypted(&password)?; + { + let mut signer = GLOBALS.signer.write().await; + signer.load_encrypted_private_key(epk.clone()); + signer.unlock_encrypted_private_key(&password)?; + } + password.zeroize(); + } + "import_hex" => { + let (mut import_hex, mut password): (String, String) = + serde_json::from_str(&bus_message.json_payload)?; + let pk = PrivateKey::try_from_hex_string(&import_hex)?; + import_hex.zeroize(); + let epk = pk.export_encrypted(&password)?; + { + let mut signer = GLOBALS.signer.write().await; + signer.load_encrypted_private_key(epk.clone()); + signer.unlock_encrypted_private_key(&password)?; + } + password.zeroize(); + } _ => {} }, _ => {} diff --git a/src/ui/mod.rs b/src/ui/mod.rs index 58becba2..82ade764 100644 --- a/src/ui/mod.rs +++ b/src/ui/mod.rs @@ -14,6 +14,7 @@ use crate::settings::Settings; use eframe::{egui, IconData, Theme}; use egui::{ColorImage, Context, ImageData, TextureHandle, TextureOptions}; use nostr_types::{PublicKey, PublicKeyHex}; +use zeroize::Zeroize; pub fn run() -> Result<(), Error> { let icon_bytes = include_bytes!("../../gossip.png"); @@ -67,6 +68,15 @@ struct GossipUi { follow_bech32_pubkey: String, follow_hex_pubkey: String, follow_pubkey_at_relay: String, + password: String, + import_bech32: String, + import_hex: String, +} + +impl Drop for GossipUi { + fn drop(&mut self) { + self.password.zeroize(); + } } impl GossipUi { @@ -122,6 +132,9 @@ impl GossipUi { follow_bech32_pubkey: "".to_owned(), follow_hex_pubkey: "".to_owned(), follow_pubkey_at_relay: "".to_owned(), + password: "".to_owned(), + import_bech32: "".to_owned(), + import_hex: "".to_owned(), } } } diff --git a/src/ui/people.rs b/src/ui/people.rs index 234539a5..6903cbd6 100644 --- a/src/ui/people.rs +++ b/src/ui/people.rs @@ -19,6 +19,8 @@ pub(super) fn update(app: &mut GossipUi, ctx: &Context, _frame: &mut eframe::Fra ui.heading("NOTICE: Gossip doesn't update the filters when you follow someone yet, so you have to restart the client to fetch their events. Will fix soon."); + ui.label("NOTICE: use CTRL-V to paste (middle/right click wont work)"); + ui.add_space(10.0); ui.separator(); ui.add_space(10.0); diff --git a/src/ui/you.rs b/src/ui/you.rs index 30c2d0f8..a7393084 100644 --- a/src/ui/you.rs +++ b/src/ui/you.rs @@ -1,7 +1,122 @@ use super::GossipUi; +use crate::comms::BusMessage; +use crate::globals::GLOBALS; use eframe::egui; -use egui::{Context, Ui}; +use egui::{Context, TextEdit, Ui}; +use nostr_types::{KeySecurity, PublicKeyHex}; +use tracing::info; +use zeroize::Zeroize; -pub(super) fn update(_app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) { - ui.heading("Your Identities"); +pub(super) fn update(app: &mut GossipUi, _ctx: &Context, _frame: &mut eframe::Frame, ui: &mut Ui) { + ui.add_space(30.0); + + ui.label("NOTICE: use CTRL-V to paste (middle/right click wont work)"); + + ui.add_space(10.0); + ui.separator(); + ui.add_space(10.0); + + if GLOBALS.signer.blocking_read().is_ready() { + ui.heading("Ready to sign events"); + + let key_security = GLOBALS.signer.blocking_read().key_security().unwrap(); + let public_key = GLOBALS.signer.blocking_read().public_key().unwrap(); + + ui.label(&*format!( + "Private Key security is {}", + match key_security { + KeySecurity::Weak => "weak", + KeySecurity::Medium => "medium", + } + )); + + let pkhex: PublicKeyHex = public_key.into(); + ui.label(&format!("Public Key: {}", pkhex.0)); + } else if GLOBALS.signer.blocking_read().is_loaded() { + ui.heading("Password Needed"); + + ui.horizontal(|ui| { + ui.label("Password: "); + ui.add(TextEdit::singleline(&mut app.password).password(true)); + }); + + if ui.button("Unlock Private Key").clicked() { + let tx = GLOBALS.to_overlord.clone(); + let _ = tx.send(BusMessage { + target: "overlord".to_string(), + kind: "unlock_key".to_string(), + json_payload: serde_json::to_string(&app.password).unwrap(), + }); + app.password.zeroize(); + app.password = "".to_owned(); + } + } else { + ui.heading("Generate a Keypair"); + + if ui.button("Generate Now").clicked() { + info!("TBD GENERATE"); + } + + ui.add_space(10.0); + ui.separator(); + ui.add_space(10.0); + + ui.heading("Import a bech32 private key"); + + ui.horizontal(|ui| { + ui.label("Enter bech32 private key"); + ui.add( + TextEdit::singleline(&mut app.import_bech32) + .hint_text("nsec1...") + .password(true), + ); + }); + ui.horizontal(|ui| { + ui.label("Enter a password to keep it encrypted under"); + ui.add(TextEdit::singleline(&mut app.password).password(true)); + }); + if ui.button("import").clicked() { + let tx = GLOBALS.to_overlord.clone(); + let _ = tx.send(BusMessage { + target: "overlord".to_string(), + kind: "import_bech32".to_string(), + json_payload: serde_json::to_string(&(&app.import_bech32, &app.password)).unwrap(), + }); + app.import_bech32.zeroize(); + app.import_bech32 = "".to_owned(); + app.password.zeroize(); + app.password = "".to_owned(); + } + + ui.add_space(10.0); + ui.separator(); + ui.add_space(10.0); + + ui.heading("Import a hex private key"); + + ui.horizontal(|ui| { + ui.label("Enter hex-encoded private key"); + ui.add( + TextEdit::singleline(&mut app.import_hex) + .hint_text("0123456789abcdef...") + .password(true), + ); + }); + ui.horizontal(|ui| { + ui.label("Enter a password to keep it encrypted under"); + ui.add(TextEdit::singleline(&mut app.password).password(true)); + }); + if ui.button("import").clicked() { + let tx = GLOBALS.to_overlord.clone(); + let _ = tx.send(BusMessage { + target: "overlord".to_string(), + kind: "import_hex".to_string(), + json_payload: serde_json::to_string(&(&app.import_hex, &app.password)).unwrap(), + }); + app.import_hex.zeroize(); + app.import_hex = "".to_owned(); + app.password.zeroize(); + app.password = "".to_owned(); + } + } }