feat(main): export and import encrypted secret key
This commit is contained in:
parent
2e76250edc
commit
828114f5de
3 changed files with 222 additions and 35 deletions
155
Cargo.lock
generated
155
Cargo.lock
generated
|
@ -38,6 +38,21 @@ dependencies = [
|
|||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm-siv"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"polyval",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.3"
|
||||
|
@ -112,6 +127,18 @@ dependencies = [
|
|||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
|
@ -311,6 +338,12 @@ dependencies = [
|
|||
"bitcoin_hashes 0.14.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.22.1"
|
||||
|
@ -335,6 +368,15 @@ version = "0.5.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
|
||||
|
||||
[[package]]
|
||||
name = "bincode"
|
||||
version = "1.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bip39"
|
||||
version = "2.1.0"
|
||||
|
@ -422,6 +464,17 @@ version = "2.8.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||
|
||||
[[package]]
|
||||
name = "blake2b_simd"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23285ad32269793932e830392f2fe2f83e26488fd3ec778883a93c8323735780"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"constant_time_eq",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
|
@ -595,6 +648,12 @@ dependencies = [
|
|||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
|
@ -647,6 +706,15 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.7.0"
|
||||
|
@ -847,6 +915,18 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "filetime"
|
||||
version = "0.2.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"libredox",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fnv"
|
||||
version = "1.0.7"
|
||||
|
@ -1362,6 +1442,7 @@ checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d"
|
|||
dependencies = [
|
||||
"bitflags",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1464,6 +1545,7 @@ dependencies = [
|
|||
name = "mostr"
|
||||
version = "0.8.0"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"chrono",
|
||||
"colog",
|
||||
"colored",
|
||||
|
@ -1478,6 +1560,7 @@ dependencies = [
|
|||
"parse_datetime",
|
||||
"regex",
|
||||
"rustyline",
|
||||
"simple_crypt",
|
||||
"tokio",
|
||||
"whoami",
|
||||
]
|
||||
|
@ -1523,7 +1606,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "af7c1eebe17dd785e52e1f81149c1b50fa6ec92e4ac239840934d1ffbd4f631c"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"base64",
|
||||
"base64 0.22.1",
|
||||
"bech32",
|
||||
"bip39",
|
||||
"bitcoin",
|
||||
|
@ -1792,6 +1875,18 @@ dependencies = [
|
|||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
|
@ -1932,6 +2027,18 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-argon2"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5885493fdf0be6cdff808d1533ce878d21cfa49c7086fa00c66355cd9141bfc"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"blake2b_simd",
|
||||
"constant_time_eq",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc-demangle"
|
||||
version = "0.1.24"
|
||||
|
@ -1990,8 +2097,8 @@ checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
|
|||
|
||||
[[package]]
|
||||
name = "rustyline"
|
||||
version = "14.0.0"
|
||||
source = "git+https://github.com/xeruf/rustyline?rev=465b14d#465b14d71b2fed2e992e212ec08560c0994e8e10"
|
||||
version = "15.0.0"
|
||||
source = "git+https://github.com/xeruf/rustyline?rev=5364854#53648543f7511fb5271afed3748f2b08cc9810f3"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
|
@ -2193,6 +2300,22 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simple_crypt"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19a335d088ffc07695a1aee7b94b72a70ba438bed139cf3f3397fcc6c102d113"
|
||||
dependencies = [
|
||||
"aes-gcm-siv",
|
||||
"anyhow",
|
||||
"bincode",
|
||||
"log",
|
||||
"rust-argon2",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"tar",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.9"
|
||||
|
@ -2264,6 +2387,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tar"
|
||||
version = "0.4.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6"
|
||||
dependencies = [
|
||||
"filetime",
|
||||
"libc",
|
||||
"xattr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.15.0"
|
||||
|
@ -2496,9 +2630,9 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
|||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
|
@ -2871,6 +3005,17 @@ version = "0.5.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
|
||||
|
||||
[[package]]
|
||||
name = "xattr"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"rustix",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xdg-home"
|
||||
version = "1.3.0"
|
||||
|
|
|
@ -22,13 +22,15 @@ log = "0.4"
|
|||
env_logger = "0.11"
|
||||
colog = "1.3"
|
||||
colored = "2.2"
|
||||
rustyline = { git = "https://github.com/xeruf/rustyline", rev = "465b14d" }
|
||||
rustyline = { git = "https://github.com/xeruf/rustyline", rev = "5364854" }
|
||||
# OS-Specific Abstractions
|
||||
keyring = "3"
|
||||
directories = "5.0"
|
||||
whoami = "1.5"
|
||||
# slint = "1.8"
|
||||
# Application Utils
|
||||
base64 = "0.22"
|
||||
simple_crypt = "0.2"
|
||||
itertools = "0.12"
|
||||
chrono = "0.4"
|
||||
parse_datetime = "0.5"
|
||||
|
|
98
src/main.rs
98
src/main.rs
|
@ -1,3 +1,27 @@
|
|||
use crate::event_sender::MostrMessage;
|
||||
use crate::hashtag::Hashtag;
|
||||
use crate::helpers::*;
|
||||
use crate::kinds::{format_tag_basic, match_event_tag, Prio, BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS};
|
||||
use crate::task::{State, StateChange, Task, MARKER_PROPERTY};
|
||||
use crate::tasks::{referenced_event, PropertyCollection, StateFilter, TasksRelay};
|
||||
|
||||
use base64::prelude::BASE64_STANDARD;
|
||||
use base64::Engine;
|
||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
||||
use colored::Colorize;
|
||||
use directories::ProjectDirs;
|
||||
use env_logger::{Builder, Target, WriteStyle};
|
||||
use itertools::Itertools;
|
||||
use keyring::Entry;
|
||||
use keyring::Error::NoEntry;
|
||||
use log::{debug, error, info, trace, warn, LevelFilter};
|
||||
use nostr_sdk::bitcoin::hex::DisplayHex;
|
||||
use nostr_sdk::prelude::*;
|
||||
use nostr_sdk::serde_json::Serializer;
|
||||
use regex::Regex;
|
||||
use rustyline::config::Configurer;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::DefaultEditor;
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::env::{args, var};
|
||||
use std::fs;
|
||||
|
@ -7,26 +31,6 @@ use std::iter::once;
|
|||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::event_sender::MostrMessage;
|
||||
use crate::hashtag::Hashtag;
|
||||
use crate::helpers::*;
|
||||
use crate::kinds::{format_tag_basic, match_event_tag, Prio, BASIC_KINDS, PROPERTY_COLUMNS, PROP_KINDS};
|
||||
use crate::task::{State, StateChange, Task, MARKER_PROPERTY};
|
||||
use crate::tasks::{referenced_event, PropertyCollection, StateFilter, TasksRelay};
|
||||
use chrono::{DateTime, Local, TimeZone, Utc};
|
||||
use colored::Colorize;
|
||||
use directories::ProjectDirs;
|
||||
use env_logger::{Builder, Target, WriteStyle};
|
||||
use itertools::Itertools;
|
||||
use keyring::Entry;
|
||||
use log::{debug, error, info, trace, warn, LevelFilter};
|
||||
use nostr_sdk::prelude::*;
|
||||
use nostr_sdk::serde_json::Serializer;
|
||||
use regex::Regex;
|
||||
use rustyline::config::Configurer;
|
||||
use rustyline::error::ReadlineError;
|
||||
use rustyline::DefaultEditor;
|
||||
use tokio::sync::mpsc;
|
||||
use tokio::time::error::Elapsed;
|
||||
use tokio::time::timeout;
|
||||
|
@ -62,13 +66,13 @@ macro_rules! or_warn {
|
|||
}
|
||||
}
|
||||
|
||||
fn read_keys(readline: &mut DefaultEditor) -> Result<Keys> {
|
||||
let keys_entry = Entry::new("mostr", "keys")?;
|
||||
fn read_keys(keys_entry: Entry, readline: &mut DefaultEditor) -> Result<Keys> {
|
||||
if let Ok(pass) = keys_entry.get_secret() {
|
||||
return Ok(SecretKey::from_slice(&pass).map(|s| Keys::new(s))
|
||||
.inspect_err(|e| eprintln!("Invalid key in keychain: {e}"))?);
|
||||
}
|
||||
let line = readline.readline("Secret key? (leave blank to generate and save a new keypair) ")?;
|
||||
let line = read_password(readline, "Secret key? (leave blank to generate and save a new keypair) ")?;
|
||||
|
||||
let keys = if line.is_empty() {
|
||||
info!("Generating and persisting new key");
|
||||
Keys::generate()
|
||||
|
@ -76,11 +80,21 @@ fn read_keys(readline: &mut DefaultEditor) -> Result<Keys> {
|
|||
Keys::from_str(&line)
|
||||
.inspect_err(|e| eprintln!("Invalid key provided: {e}"))?
|
||||
};
|
||||
or_warn!(keys_entry.set_secret(keys.secret_key().as_secret_bytes()),
|
||||
"Could not persist keys");
|
||||
if let Err(e) = keys_entry.set_secret(keys.secret_key().as_secret_bytes()) {
|
||||
if line.is_empty() {
|
||||
return Err(e.into());
|
||||
} else {
|
||||
warn!("Could not persist keys: {}", e)
|
||||
}
|
||||
}
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
fn read_password(readline: &mut DefaultEditor, prompt: &str) -> Result<String> {
|
||||
let line = readline.readline(prompt)?;
|
||||
|
||||
Ok(line)
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
|
@ -104,7 +118,6 @@ async fn main() -> Result<()> {
|
|||
};
|
||||
|
||||
let mut rl = DefaultEditor::new()?;
|
||||
rl.set_auto_add_history(true);
|
||||
or_warn!(
|
||||
rl.create_external_writer().map(
|
||||
|wr| builder
|
||||
|
@ -136,8 +149,34 @@ async fn main() -> Result<()> {
|
|||
.inspect(|_| { or_warn!(fs::remove_file(key_file)); }));
|
||||
}
|
||||
|
||||
let keys = read_keys(&mut rl)?;
|
||||
let relays_file = config_dir.join("relays");
|
||||
let keys_entry = Entry::new("mostr", "keys")?;
|
||||
let keys =
|
||||
if args.peek().is_some_and(|arg| arg.trim_start_matches('-') == "import") {
|
||||
let key = rl.readline("Enter your encrypted or plaintext secret key: ")?;
|
||||
|
||||
let mut guard = rl.set_cursor_visibility(false)?;
|
||||
let enc_pwd = read_password(&mut rl, "Please enter the encryption password you used: ")?;
|
||||
guard.take();
|
||||
|
||||
let data = simple_crypt::decrypt(&(BASE64_STANDARD.decode(key)?), enc_pwd.as_bytes())?;
|
||||
let keys = Keys::new(SecretKey::from_slice(&data)?);
|
||||
if keys_entry.get_secret().is_err_and(|e| matches!(e, NoEntry)) ||
|
||||
rl.readline(&format!("Override stored key with given keypair, public key: {} (y/n)? ", keys.public_key()))? == "y" {
|
||||
keys_entry.set_secret(keys.secret_key().as_secret_bytes())?;
|
||||
}
|
||||
keys
|
||||
} else {
|
||||
read_keys(keys_entry, &mut rl)?
|
||||
};
|
||||
|
||||
info!("My active public key: {}", keys.public_key());
|
||||
if args.peek().is_some_and(|arg| arg.trim_start_matches('-') == "export") {
|
||||
let enc_pwd = read_password(&mut rl, "Please enter an encryption password for your secret key: ")?;
|
||||
let data = simple_crypt::encrypt(keys.secret_key().as_secret_bytes(), enc_pwd.as_bytes())?;
|
||||
println!("Your encrypted key: {}", BASE64_STANDARD.encode(&data));
|
||||
// TODO optionally delete
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let client = ClientBuilder::new()
|
||||
.opts(Options::new()
|
||||
|
@ -146,8 +185,8 @@ async fn main() -> Result<()> {
|
|||
)
|
||||
.signer(keys.clone())
|
||||
.build();
|
||||
info!("My public key: {}", keys.public_key());
|
||||
|
||||
let relays_file = config_dir.join("relays");
|
||||
// TODO use NewRelay message for all relays
|
||||
match var("MOSTR_RELAY") {
|
||||
Ok(relay) => {
|
||||
|
@ -268,6 +307,7 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
}
|
||||
|
||||
rl.set_auto_add_history(true);
|
||||
'repl: loop {
|
||||
println!();
|
||||
let tasks = relays.get(&selected_relay).unwrap();
|
||||
|
|
Loading…
Add table
Reference in a new issue