-
Notifications
You must be signed in to change notification settings - Fork 79
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feat/os keychain followup #1770
base: main
Are you sure you want to change the base?
Changes from all commits
4c743e2
0914f06
b1f7bca
5428c3d
16cfacd
5b763f2
8e234bc
b59fc3d
9601f22
c1423c3
9ff4022
6280d68
a7a4d66
ba652d0
e983f6c
e470926
c66f567
e14ff6d
e5eab8f
a1e4ec4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,14 +1,17 @@ | ||
use std::io::Write; | ||
|
||
use clap::command; | ||
use sep5::SeedPhrase; | ||
|
||
use crate::{ | ||
commands::global, | ||
config::{ | ||
address::KeyName, | ||
key::{self, Key}, | ||
locator, | ||
key, locator, | ||
secret::{self, Secret}, | ||
}, | ||
print::Print, | ||
signer::secure_store, | ||
}; | ||
|
||
#[derive(thiserror::Error, Debug)] | ||
|
@@ -19,6 +22,15 @@ pub enum Error { | |
Key(#[from] key::Error), | ||
#[error(transparent)] | ||
Config(#[from] locator::Error), | ||
|
||
#[error(transparent)] | ||
SecureStore(#[from] secure_store::Error), | ||
|
||
#[error(transparent)] | ||
SeedPhrase(#[from] sep5::error::Error), | ||
|
||
#[error("secret input error")] | ||
PasswordRead, | ||
} | ||
|
||
#[derive(Debug, clap::Parser, Clone)] | ||
|
@@ -40,26 +52,56 @@ pub struct Cmd { | |
|
||
impl Cmd { | ||
pub fn run(&self, global_args: &global::Args) -> Result<(), Error> { | ||
let print = Print::new(global_args.quiet); | ||
let key = if let Some(key) = self.public_key.as_ref() { | ||
key.parse()? | ||
} else { | ||
self.secrets.read_secret()?.into() | ||
self.read_secret(&print)?.into() | ||
}; | ||
|
||
let print = Print::new(global_args.quiet); | ||
let path = self.config_locator.write_key(&self.name, &key)?; | ||
|
||
if let Key::Secret(Secret::SeedPhrase { seed_phrase }) = key { | ||
if seed_phrase.split_whitespace().count() < 24 { | ||
print.checkln(format!("Key saved with alias {:?} in {path:?}", self.name)); | ||
|
||
Ok(()) | ||
} | ||
|
||
fn read_secret(&self, print: &Print) -> Result<Secret, Error> { | ||
if let Ok(secret_key) = std::env::var("SOROBAN_SECRET_KEY") { | ||
Ok(Secret::SecretKey { secret_key }) | ||
} else if self.secrets.secure_store { | ||
let prompt = "Type a 12/24 word seed phrase:"; | ||
let secret_key = read_password(print, prompt)?; | ||
if secret_key.split_whitespace().count() < 24 { | ||
print.warnln("The provided seed phrase lacks sufficient entropy and should be avoided. Using a 24-word seed phrase is a safer option.".to_string()); | ||
print.warnln( | ||
"To generate a new key, use the `stellar keys generate` command.".to_string(), | ||
); | ||
} | ||
} | ||
|
||
print.checkln(format!("Key saved with alias {:?} in {path:?}", self.name)); | ||
let seed_phrase: SeedPhrase = secret_key.parse()?; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. At the moment I only added support for a user to add a new seed phrase to secure storage. I'm happy to make a change so that this supports adding a private key directly as well, but wanted to get some more eyes on this to see if folks thought it was worth the effort. |
||
|
||
Ok(()) | ||
Ok(secure_store::save_secret(print, &self.name, seed_phrase)?) | ||
} else { | ||
let prompt = "Type a secret key or 12/24 word seed phrase:"; | ||
let secret_key = read_password(print, prompt)?; | ||
let secret = secret_key.parse()?; | ||
if let Secret::SeedPhrase { seed_phrase } = &secret { | ||
if seed_phrase.split_whitespace().count() < 24 { | ||
print.warnln("The provided seed phrase lacks sufficient entropy and should be avoided. Using a 24-word seed phrase is a safer option.".to_string()); | ||
print.warnln( | ||
"To generate a new key, use the `stellar keys generate` command." | ||
.to_string(), | ||
); | ||
} | ||
} | ||
Ok(secret) | ||
} | ||
} | ||
} | ||
|
||
fn read_password(print: &Print, prompt: &str) -> Result<String, Error> { | ||
print.arrowln(prompt); | ||
std::io::stdout().flush().map_err(|_| Error::PasswordRead)?; | ||
rpassword::read_password().map_err(|_| Error::PasswordRead) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,12 +6,7 @@ use super::super::config::{ | |
secret::{self, Secret}, | ||
}; | ||
|
||
use crate::{ | ||
commands::global, | ||
config::address::KeyName, | ||
print::Print, | ||
signer::keyring::{self, StellarEntry}, | ||
}; | ||
use crate::{commands::global, config::address::KeyName, print::Print, signer::secure_store}; | ||
|
||
#[derive(thiserror::Error, Debug)] | ||
pub enum Error { | ||
|
@@ -28,7 +23,7 @@ pub enum Error { | |
IdentityAlreadyExists(String), | ||
|
||
#[error(transparent)] | ||
Keyring(#[from] keyring::Error), | ||
SecureStore(#[from] secure_store::Error), | ||
} | ||
|
||
#[derive(Debug, clap::Parser, Clone)] | ||
|
@@ -125,29 +120,13 @@ impl Cmd { | |
fn secret(&self, print: &Print) -> Result<Secret, Error> { | ||
let seed_phrase = self.seed_phrase()?; | ||
if self.secure_store { | ||
// secure_store:org.stellar.cli:<key name> | ||
let entry_name_with_prefix = format!( | ||
"{}{}-{}", | ||
keyring::SECURE_STORE_ENTRY_PREFIX, | ||
keyring::SECURE_STORE_ENTRY_SERVICE, | ||
self.name | ||
); | ||
|
||
//checking that the entry name is valid before writing to the secure store | ||
let secret: Secret = entry_name_with_prefix.parse()?; | ||
|
||
if let Secret::SecureStore { entry_name } = &secret { | ||
Self::write_to_secure_store(entry_name, seed_phrase, print)?; | ||
} | ||
|
||
return Ok(secret); | ||
} | ||
let secret: Secret = seed_phrase.into(); | ||
Ok(if self.as_secret { | ||
secret.private_key(self.hd_path)?.into() | ||
Ok(secure_store::save_secret(print, &self.name, seed_phrase)?) | ||
} else if self.as_secret { | ||
let secret: Secret = seed_phrase.into(); | ||
Ok(secret.private_key(self.hd_path)?.into()) | ||
} else { | ||
secret | ||
}) | ||
Ok(seed_phrase.into()) | ||
} | ||
} | ||
|
||
fn seed_phrase(&self) -> Result<SeedPhrase, Error> { | ||
|
@@ -157,24 +136,6 @@ impl Cmd { | |
secret::seed_phrase_from_seed(self.seed.as_deref()) | ||
}?) | ||
} | ||
|
||
fn write_to_secure_store( | ||
entry_name: &String, | ||
seed_phrase: SeedPhrase, | ||
print: &Print, | ||
) -> Result<(), Error> { | ||
print.infoln(format!("Writing to secure store: {entry_name}")); | ||
let entry = StellarEntry::new(entry_name)?; | ||
if let Ok(key) = entry.get_public_key(None) { | ||
print.warnln(format!("A key for {entry_name} already exists in your operating system's secure store: {key}")); | ||
} else { | ||
print.infoln(format!( | ||
"Saving a new key to your operating system's secure store: {entry_name}" | ||
)); | ||
entry.set_seed_phrase(seed_phrase)?; | ||
} | ||
Ok(()) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was all moved to a secure_store mod |
||
} | ||
|
||
#[cfg(test)] | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved this here from
config/secret.rs
- i was seeing an interesting cyclical dependency, and moving this here helped that be a bit less confusing.