diff --git a/Cargo.lock b/Cargo.lock index 25b8f6c321..8ec14b811c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -652,6 +652,31 @@ version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +[[package]] +name = "crossterm" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +dependencies = [ + "bitflags 2.4.1", + "crossterm_winapi", + "libc", + "mio", + "parking_lot", + "signal-hook", + "signal-hook-mio", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.2" @@ -1475,6 +1500,7 @@ dependencies = [ "color-backtrace", "colored", "console", + "crossterm", "dirs 5.0.1", "dotenvy", "indexmap 1.9.3", @@ -1686,6 +1712,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f3d0b296e374a4e6f3c7b0a1f5a51d748a0d34c85e7dc48fc3fa9a87657fe09" dependencies = [ "libc", + "log", "wasi", "windows-sys 0.48.0", ] @@ -2566,6 +2593,36 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-mio" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +dependencies = [ + "libc", + "mio", + "signal-hook", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "signature" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index 3ec84a10d9..2016d95910 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -167,6 +167,9 @@ features = [ "fmt" ] [dependencies.zip] version = "^0.6" +[dependencies.crossterm] +version = "0.27.0" + [target."cfg(windows)".dependencies.ansi_term] version = "0.12.1" diff --git a/leo/cli/commands/account.rs b/leo/cli/commands/account.rs index eea0ed6883..0666bbe2fe 100644 --- a/leo/cli/commands/account.rs +++ b/leo/cli/commands/account.rs @@ -18,8 +18,10 @@ use super::*; use leo_package::root::Env; use snarkvm::prelude::{Address, PrivateKey, ViewKey}; +use crossterm::ExecutableCommand; use rand::SeedableRng; use rand_chacha::ChaChaRng; +use std::io::{self, Read, Write}; /// Commands to manage Aleo accounts. #[derive(Parser, Debug)] @@ -32,6 +34,9 @@ pub enum Account { /// Write the private key to the .env file. #[clap(short = 'w', long)] write: bool, + /// Print sensitive information (such as private key) discreetly to an alternate screen + #[clap(long)] + discreet: bool, }, /// Derive an Aleo account from a private key. Import { @@ -40,6 +45,9 @@ pub enum Account { /// Write the private key to the .env file. #[clap(short = 'w', long)] write: bool, + /// Print sensitive information (such as private key) discreetly to an alternate screen + #[clap(long)] + discreet: bool, }, } @@ -59,7 +67,7 @@ impl Command for Account { Self: Sized, { match self { - Account::New { seed, write } => { + Account::New { seed, write, discreet } => { // Sample a new Aleo account. let private_key = match seed { // Recover the field element deterministically. @@ -70,16 +78,16 @@ impl Command for Account { .map_err(CliError::failed_to_parse_seed)?; // Derive the view key and address and print to stdout. - print_keys(private_key)?; + print_keys(private_key, discreet)?; // Save key data to .env file. if write { write_to_env_file(private_key, &ctx)?; } } - Account::Import { private_key, write } => { + Account::Import { private_key, write, discreet } => { // Derive the view key and address and print to stdout. - print_keys(private_key)?; + print_keys(private_key, discreet)?; // Save key data to .env file. if write { @@ -94,16 +102,24 @@ impl Command for Account { // Helper functions // Print keys as a formatted string without log level. -fn print_keys(private_key: PrivateKey) -> Result<()> { +fn print_keys(private_key: PrivateKey, discreet: bool) -> Result<()> { let view_key = ViewKey::try_from(&private_key)?; let address = Address::::try_from(&view_key)?; - println!( - "\n {:>12} {private_key}\n {:>12} {view_key}\n {:>12} {address}\n", - "Private Key".cyan().bold(), - "View Key".cyan().bold(), - "Address".cyan().bold(), - ); + if !discreet { + println!( + "\n {:>12} {private_key}\n {:>12} {view_key}\n {:>12} {address}\n", + "Private Key".cyan().bold(), + "View Key".cyan().bold(), + "Address".cyan().bold(), + ); + return Ok(()); + } + display_string_discreetly( + &private_key.to_string(), + "### Do not share or lose this private key! Press any key to complete. ###", + )?; + println!("\n {:>12} {view_key}\n {:>12} {address}\n", "View Key".cyan().bold(), "Address".cyan().bold(),); Ok(()) } @@ -115,3 +131,24 @@ fn write_to_env_file(private_key: PrivateKey, ctx: &Context) -> tracing::info!("✅ Private Key written to {}", program_dir.join(".env").display()); Ok(()) } + +/// Print the string to an alternate screen, so that the string won't been printed to the terminal. +fn display_string_discreetly(discreet_string: &str, continue_message: &str) -> Result<()> { + use crossterm::{ + style::Print, + terminal::{EnterAlternateScreen, LeaveAlternateScreen}, + }; + let mut stdout = io::stdout(); + stdout.execute(EnterAlternateScreen).unwrap(); + // print msg on the alternate screen + stdout.execute(Print(format!("{discreet_string}\n{continue_message}"))).unwrap(); + stdout.flush().unwrap(); + wait_for_keypress(); + stdout.execute(LeaveAlternateScreen).unwrap(); + Ok(()) +} + +fn wait_for_keypress() { + let mut single_key = [0u8]; + std::io::stdin().read_exact(&mut single_key).unwrap(); +}