upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/login.rs
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2023-09-01 00:00:00 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2023-09-01 00:00:00 +0000
commit96660a90e4cd296a2922d7a547de4cd9d0b1928b (patch)
treee5216e22ee1a3e1653d8d1ecd856f4f03615d6a1 /src/login.rs
parent6423baebd92e45c9be85157c443dff42e65d8d14 (diff)
feat(login) password login using encrypted nsec
Enables the user to only handle the nsec upon first use of the tool by encrypting it with a password and storing it on disk in an application cache. The approach to encryption draws heavily from that used by the gossip nostr client. - unencrypted nsec is zeroed from memory - a salt is used to defend against rainbow tables - computationally expensive key stretching defends against brute-force attacks of passwords with low entropy. There is UX trade-off between decryption speed and key-stretching computation. This UX challenge is exacerbated in a cli tool as decryption must take place more regularly. Thought was put into the selected n_log and a heavily reduced value is provided for long passwords where security benefits are smaller. A more granular reducing in computation was also considered by rejected to avoided to revealing just how weak a password is as most weak passwords are reused.
Diffstat (limited to 'src/login.rs')
-rw-r--r--src/login.rs83
1 files changed, 76 insertions, 7 deletions
diff --git a/src/login.rs b/src/login.rs
index da19a75..a6ce76d 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -1,16 +1,85 @@
1use anyhow::{Context, Result}; 1use anyhow::{bail, Context, Result};
2use nostr::prelude::{FromSkStr, ToBech32};
3use zeroize::Zeroize;
2 4
3use crate::{ 5use crate::{
6 cli_interactor::{Interactor, InteractorPrompt, PromptPasswordParms},
4 config::{ConfigManagement, ConfigManager}, 7 config::{ConfigManagement, ConfigManager},
5 key_handling::users::{UserManagement, UserManager}, 8 key_handling::{
9 encryption::{EncryptDecrypt, Encryptor},
10 users::{UserManagement, UserManager},
11 },
6}; 12};
7 13
8pub fn launch(nsec: &Option<String>) -> Result<()> { 14/// handles the encrpytion and storage of key material
15pub fn launch(nsec: &Option<String>, password: &Option<String>) -> Result<nostr::Keys> {
16 // if nsec parameter
17 if let Some(nsec_unwrapped) = nsec {
18 // get key or fail without prompts
19 let key = nostr::Keys::from_sk_str(nsec_unwrapped).context("invalid nsec parameter")?;
20 println!(
21 "logged in as {}",
22 &key.public_key()
23 .to_bech32()
24 .context("public key should always produce bech32")?
25 );
26
27 // if password, add user to enable password login in future
28 if password.is_some() {
29 UserManager::default()
30 .add(nsec, password)
31 .context("could not store identity")?;
32 }
33 return Ok(key);
34 }
35
36 // if encrypted nsec stored, attempt password
9 let cfg = ConfigManager 37 let cfg = ConfigManager
10 .load() 38 .load()
11 .context("failed to load application config")?; 39 .context("failed to load application config")?;
12 if !cfg.users.is_empty() { 40 let key = if let Some(user) = cfg.users.last() {
13 println!("logged in as {}", cfg.users[0].nsec); 41 let mut pass = if let Some(p) = password.clone() {
14 } 42 p
15 UserManager::default().add(nsec) 43 } else {
44 println!(
45 "login as {}",
46 &user
47 .public_key
48 .to_bech32()
49 .context("public key should always produce bech32")?
50 );
51 Interactor::default()
52 .password(PromptPasswordParms::default().with_prompt("password"))
53 .context("failed to get password input from interactor.password")?
54 };
55
56 let key_result = Encryptor
57 .decrypt_key(&user.encrypted_key, pass.as_str())
58 .context("failed to decrypt key with provided password");
59 pass.zeroize();
60
61 key_result.context(format!(
62 "failed to log in as {}",
63 &user
64 .public_key
65 .to_bech32()
66 .context("public key should always produce bech32")?
67 ))?
68 } else {
69 // no nsec but password supplied
70 if password.is_some() {
71 bail!("no nsec available to decrypt with specified password");
72 }
73 // otherwise add new user with nsec and password prompts
74 UserManager::default()
75 .add(nsec, password)
76 .context("failed to add user")?
77 };
78 println!(
79 "logged in as {}",
80 &key.public_key()
81 .to_bech32()
82 .context("public key should always produce bech32")?
83 );
84 Ok(key)
16} 85}