solana-keygen - Poor mans keypair encryption (#6259)

* SDK: Refactor (read|write)_keypair

Split file opening and data writing operations
Drop filename == "-" stdio signal. It is an app-level feature

* keygen: Move all non-key printing to stderr

* keygen: Adapt to SDK refactor

* keygen: Factor keypair output out to a helper function
This commit is contained in:
Trent Nelson 2019-10-10 17:01:03 -06:00 committed by GitHub
parent f8b36f4658
commit 9cde67086f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 95 additions and 69 deletions

View File

@ -1,7 +1,7 @@
use clap::{crate_description, crate_name, crate_version, value_t, App, Arg, ArgMatches};
use solana_core::gen_keys::GenKeys;
use solana_drone::drone::DRONE_PORT;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
use std::net::SocketAddr;
use std::process::exit;
use std::time::Duration;
@ -179,7 +179,7 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
});
if matches.is_present("identity") {
args.identity = read_keypair(matches.value_of("identity").unwrap())
args.identity = read_keypair_file(matches.value_of("identity").unwrap())
.expect("can't read client identity");
} else {
args.identity = {

View File

@ -1,7 +1,7 @@
use clap::{crate_description, crate_name, crate_version, App, Arg, ArgMatches};
use solana_drone::drone::DRONE_PORT;
use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
use std::{net::SocketAddr, process::exit, time::Duration};
const NUM_LAMPORTS_PER_ACCOUNT_DEFAULT: u64 = 64 * 1024;
@ -181,7 +181,7 @@ pub fn extract_args<'a>(matches: &ArgMatches<'a>) -> Config {
}
if matches.is_present("identity") {
args.id = read_keypair(matches.value_of("identity").unwrap())
args.id = read_keypair_file(matches.value_of("identity").unwrap())
.expect("can't read client identity");
}

View File

@ -1385,7 +1385,7 @@ mod tests {
use serde_json::Value;
use solana_client::mock_rpc_client_request::SIGNATURE;
use solana_sdk::{
signature::{gen_keypair_file, read_keypair},
signature::{gen_keypair_file, read_keypair_file},
transaction::TransactionError,
};
use std::path::PathBuf;
@ -1442,7 +1442,7 @@ mod tests {
// Test Balance Subcommand, incl pubkey and keypair-file inputs
let keypair_file = make_tmp_path("keypair_file");
gen_keypair_file(&keypair_file).unwrap();
let keypair = read_keypair(&keypair_file).unwrap();
let keypair = read_keypair_file(&keypair_file).unwrap();
let test_balance = test_commands.clone().get_matches_from(vec![
"test",
"balance",

View File

@ -2,7 +2,7 @@ use clap::ArgMatches;
use solana_sdk::{
native_token::sol_to_lamports,
pubkey::Pubkey,
signature::{read_keypair, Keypair, KeypairUtil},
signature::{read_keypair_file, Keypair, KeypairUtil},
};
// Return parsed values from matches at `name`
@ -32,7 +32,7 @@ where
// Return the keypair for an argument with filename `name` or None if not present.
pub fn keypair_of(matches: &ArgMatches<'_>, name: &str) -> Option<Keypair> {
if let Some(value) = matches.value_of(name) {
read_keypair(value).ok()
read_keypair_file(value).ok()
} else {
None
}
@ -56,7 +56,7 @@ pub fn amount_of(matches: &ArgMatches<'_>, name: &str, unit: &str) -> Option<u64
mod tests {
use super::*;
use clap::{App, Arg};
use solana_sdk::signature::write_keypair;
use solana_sdk::signature::write_keypair_file;
use std::fs;
fn app<'ab, 'v>() -> App<'ab, 'v> {
@ -120,7 +120,7 @@ mod tests {
fn test_keypair_of() {
let keypair = Keypair::new();
let outfile = tmp_file_path("test_gen_keypair_file.json", &keypair.pubkey());
let _ = write_keypair(&keypair, &outfile).unwrap();
let _ = write_keypair_file(&keypair, &outfile).unwrap();
let matches = app()
.clone()
@ -141,7 +141,7 @@ mod tests {
fn test_pubkey_of() {
let keypair = Keypair::new();
let outfile = tmp_file_path("test_gen_keypair_file.json", &keypair.pubkey());
let _ = write_keypair(&keypair, &outfile).unwrap();
let _ = write_keypair_file(&keypair, &outfile).unwrap();
let matches = app()
.clone()

View File

@ -1,5 +1,5 @@
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::read_keypair;
use solana_sdk::signature::read_keypair_file;
// Return an error if a pubkey cannot be parsed.
pub fn is_pubkey(string: String) -> Result<(), String> {
@ -11,7 +11,7 @@ pub fn is_pubkey(string: String) -> Result<(), String> {
// Return an error if a keypair file cannot be parsed.
pub fn is_keypair(string: String) -> Result<(), String> {
read_keypair(&string)
read_keypair_file(&string)
.map(|_| ())
.map_err(|err| format!("{:?}", err))
}

View File

@ -6,7 +6,7 @@ use solana_cli::{
display::{println_name_value, println_name_value_or},
input_validators::is_url,
};
use solana_sdk::signature::{read_keypair, KeypairUtil};
use solana_sdk::signature::{read_keypair_file, KeypairUtil};
use std::error;
fn parse_settings(matches: &ArgMatches<'_>) -> Result<bool, Box<dyn error::Error>> {
@ -94,7 +94,7 @@ pub fn parse_args(matches: &ArgMatches<'_>) -> Result<CliConfig, Box<dyn error::
}
default.keypair_path
};
let keypair = read_keypair(&keypair_path).or_else(|err| {
let keypair = read_keypair_file(&keypair_path).or_else(|err| {
Err(CliError::BadParameter(format!(
"{}: Unable to open keypair file: {}",
err, keypair_path

View File

@ -1,7 +1,7 @@
use clap::{crate_description, crate_name, crate_version, App, Arg};
use solana_drone::drone::{run_drone, Drone, DRONE_PORT};
use solana_drone::socketaddr;
use solana_sdk::signature::read_keypair;
use solana_sdk::signature::read_keypair_file;
use std::error;
use std::net::{Ipv4Addr, SocketAddr};
use std::sync::{Arc, Mutex};
@ -38,8 +38,8 @@ fn main() -> Result<(), Box<dyn error::Error>> {
)
.get_matches();
let mint_keypair =
read_keypair(matches.value_of("keypair").unwrap()).expect("failed to read client keypair");
let mint_keypair = read_keypair_file(matches.value_of("keypair").unwrap())
.expect("failed to read client keypair");
let time_slice: Option<u64>;
if let Some(secs) = matches.value_of("slice") {

View File

@ -14,7 +14,7 @@ use solana_sdk::{
poh_config::PohConfig,
pubkey::Pubkey,
rent_calculator::RentCalculator,
signature::{read_keypair, Keypair, KeypairUtil},
signature::{read_keypair_file, Keypair, KeypairUtil},
system_program, timing,
};
use solana_stake_api::stake_state;
@ -298,11 +298,11 @@ fn main() -> Result<(), Box<dyn error::Error>> {
let bootstrap_leader_stake_lamports =
value_t_or_exit!(matches, "bootstrap_leader_stake_lamports", u64);
let bootstrap_leader_keypair = read_keypair(bootstrap_leader_keypair_file)?;
let bootstrap_vote_keypair = read_keypair(bootstrap_vote_keypair_file)?;
let bootstrap_stake_keypair = read_keypair(bootstrap_stake_keypair_file)?;
let bootstrap_storage_keypair = read_keypair(bootstrap_storage_keypair_file)?;
let mint_keypair = read_keypair(mint_keypair_file)?;
let bootstrap_leader_keypair = read_keypair_file(bootstrap_leader_keypair_file)?;
let bootstrap_vote_keypair = read_keypair_file(bootstrap_vote_keypair_file)?;
let bootstrap_stake_keypair = read_keypair_file(bootstrap_stake_keypair_file)?;
let bootstrap_storage_keypair = read_keypair_file(bootstrap_storage_keypair_file)?;
let mint_keypair = read_keypair_file(mint_keypair_file)?;
let vote_account = vote_state::create_account(
&bootstrap_vote_keypair.pubkey(),

View File

@ -9,7 +9,7 @@ use solana_client::rpc_client::RpcClient;
use solana_config_api::{config_instruction, get_config_data};
use solana_sdk::message::Message;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil, Signable};
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil, Signable};
use solana_sdk::transaction::Transaction;
use std::fs::{self, File};
use std::io::{self, BufReader, Read};
@ -626,9 +626,9 @@ pub fn deploy(
download_url: &str,
update_manifest_keypair_file: &str,
) -> Result<(), String> {
let from_keypair = read_keypair(from_keypair_file)
let from_keypair = read_keypair_file(from_keypair_file)
.map_err(|err| format!("Unable to read {}: {}", from_keypair_file, err))?;
let update_manifest_keypair = read_keypair(update_manifest_keypair_file)
let update_manifest_keypair = read_keypair_file(update_manifest_keypair_file)
.map_err(|err| format!("Unable to read {}: {}", update_manifest_keypair_file, err))?;
println_name_value("JSON RPC URL:", json_rpc_url);

View File

@ -3,7 +3,10 @@ use clap::{
crate_description, crate_name, crate_version, App, AppSettings, Arg, ArgMatches, SubCommand,
};
use solana_sdk::pubkey::write_pubkey;
use solana_sdk::signature::{keypair_from_seed, read_keypair, write_keypair, KeypairUtil};
use solana_sdk::signature::{
keypair_from_seed, read_keypair, read_keypair_file, write_keypair, write_keypair_file, Keypair,
KeypairUtil,
};
use std::error;
use std::path::Path;
use std::process::exit;
@ -18,6 +21,21 @@ fn check_for_overwrite(outfile: &str, matches: &ArgMatches) {
}
}
fn output_keypair(
keypair: &Keypair,
outfile: &str,
source: &str,
) -> Result<(), Box<dyn error::Error>> {
if outfile == "-" {
let mut stdout = std::io::stdout();
write_keypair(&keypair, &mut stdout)?;
} else {
write_keypair_file(&keypair, outfile)?;
eprintln!("Wrote {} keypair to {}", source, outfile);
}
Ok(())
}
fn main() -> Result<(), Box<dyn error::Error>> {
let matches = App::new(crate_name!())
.about(crate_description!())
@ -45,7 +63,7 @@ fn main() -> Result<(), Box<dyn error::Error>> {
Arg::with_name("silent")
.short("s")
.long("silent")
.help("Do not display mnemonic phrase"),
.help("Do not display mnemonic phrase. Useful when piping output to other programs that prompt for user input, like gpg"),
),
)
.subcommand(
@ -104,7 +122,12 @@ fn main() -> Result<(), Box<dyn error::Error>> {
path.extend(&[".config", "solana", "id.json"]);
path.to_str().unwrap()
};
let keypair = read_keypair(infile)?;
let keypair = if infile == "-" {
let mut stdin = std::io::stdin();
read_keypair(&mut stdin)?
} else {
read_keypair_file(infile)?
};
if matches.is_present("outfile") {
let outfile = matches.value_of("outfile").unwrap();
@ -132,17 +155,12 @@ fn main() -> Result<(), Box<dyn error::Error>> {
let seed = Seed::new(&mnemonic, NO_PASSPHRASE);
let keypair = keypair_from_seed(seed.as_bytes())?;
let serialized_keypair = write_keypair(&keypair, outfile)?;
if outfile == "-" {
println!("{}", serialized_keypair);
} else {
println!("Wrote new keypair to {}", outfile);
}
output_keypair(&keypair, &outfile, "new")?;
let silent = matches.is_present("silent");
if !silent {
let divider = String::from_utf8(vec![b'='; phrase.len()]).unwrap();
println!(
eprintln!(
"{}\nSave this mnemonic phrase to recover your new keypair:\n{}\n{}",
&divider, phrase, &divider
);
@ -161,17 +179,12 @@ fn main() -> Result<(), Box<dyn error::Error>> {
check_for_overwrite(&outfile, &matches);
}
let phrase = rpassword::prompt_password_stdout("Mnemonic recovery phrase: ").unwrap();
let phrase = rpassword::prompt_password_stderr("Mnemonic recovery phrase: ").unwrap();
let mnemonic = Mnemonic::from_phrase(phrase.trim(), Language::English)?;
let seed = Seed::new(&mnemonic, NO_PASSPHRASE);
let keypair = keypair_from_seed(seed.as_bytes())?;
let serialized_keypair = write_keypair(&keypair, outfile)?;
if outfile == "-" {
println!("{}", serialized_keypair);
} else {
println!("Wrote recovered keypair to {}", outfile);
}
output_keypair(&keypair, &outfile, "recovered")?;
}
_ => unreachable!(),
}

View File

@ -3,7 +3,7 @@ use console::style;
use solana_core::cluster_info::{Node, FULLNODE_PORT_RANGE};
use solana_core::contact_info::ContactInfo;
use solana_core::replicator::Replicator;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
use std::net::SocketAddr;
use std::path::PathBuf;
use std::process::exit;
@ -11,7 +11,7 @@ use std::sync::Arc;
// Return an error if a keypair file cannot be parsed.
fn is_keypair(string: String) -> Result<(), String> {
read_keypair(&string)
read_keypair_file(&string)
.map(|_| ())
.map_err(|err| format!("{:?}", err))
}
@ -65,7 +65,7 @@ fn main() {
let ledger_path = PathBuf::from(matches.value_of("ledger").unwrap());
let keypair = if let Some(identity) = matches.value_of("identity") {
read_keypair(identity).unwrap_or_else(|err| {
read_keypair_file(identity).unwrap_or_else(|err| {
eprintln!("{}: Unable to open keypair file: {}", err, identity);
exit(1);
})
@ -73,7 +73,7 @@ fn main() {
Keypair::new()
};
let storage_keypair = if let Some(storage_keypair) = matches.value_of("storage_keypair") {
read_keypair(storage_keypair).unwrap_or_else(|err| {
read_keypair_file(storage_keypair).unwrap_or_else(|err| {
eprintln!("{}: Unable to open keypair file: {}", err, storage_keypair);
exit(1);
})

View File

@ -12,7 +12,7 @@ use std::borrow::Cow;
use std::error;
use std::fmt;
use std::fs::{self, File};
use std::io::Write;
use std::io::{Read, Write};
use std::mem;
use std::path::Path;
use std::str::FromStr;
@ -125,28 +125,41 @@ impl KeypairUtil for Keypair {
}
}
pub fn read_keypair(path: &str) -> Result<Keypair, Box<dyn error::Error>> {
let file = File::open(path.to_string())?;
let bytes: Vec<u8> = serde_json::from_reader(file)?;
pub fn read_keypair<R: Read>(reader: &mut R) -> Result<Keypair, Box<dyn error::Error>> {
let bytes: Vec<u8> = serde_json::from_reader(reader)?;
let keypair = Keypair::from_bytes(&bytes)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e.to_string()))?;
Ok(keypair)
}
pub fn write_keypair(keypair: &Keypair, outfile: &str) -> Result<String, Box<dyn error::Error>> {
pub fn read_keypair_file(path: &str) -> Result<Keypair, Box<dyn error::Error>> {
assert!(path != "-");
let mut file = File::open(path.to_string())?;
read_keypair(&mut file)
}
pub fn write_keypair<W: Write>(
keypair: &Keypair,
writer: &mut W,
) -> Result<String, Box<dyn error::Error>> {
let keypair_bytes = keypair.to_bytes();
let serialized = serde_json::to_string(&keypair_bytes.to_vec())?;
if outfile != "-" {
if let Some(outdir) = Path::new(outfile).parent() {
fs::create_dir_all(outdir)?;
}
let mut f = File::create(outfile)?;
f.write_all(&serialized.clone().into_bytes())?;
}
writer.write_all(&serialized.clone().into_bytes())?;
Ok(serialized)
}
pub fn write_keypair_file(
keypair: &Keypair,
outfile: &str,
) -> Result<String, Box<dyn error::Error>> {
assert!(outfile != "-");
if let Some(outdir) = Path::new(outfile).parent() {
fs::create_dir_all(outdir)?;
}
let mut f = File::create(outfile)?;
write_keypair(keypair, &mut f)
}
pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>> {
if seed.len() < ed25519_dalek::SECRET_KEY_LENGTH {
return Err("Seed is too short".into());
@ -159,7 +172,7 @@ pub fn keypair_from_seed(seed: &[u8]) -> Result<Keypair, Box<dyn error::Error>>
}
pub fn gen_keypair_file(outfile: &str) -> Result<String, Box<dyn error::Error>> {
write_keypair(&Keypair::new(), outfile)
write_keypair_file(&Keypair::new(), outfile)
}
#[cfg(test)]
@ -183,10 +196,10 @@ mod tests {
assert!(Path::new(&outfile).exists());
assert_eq!(
keypair_vec,
read_keypair(&outfile).unwrap().to_bytes().to_vec()
read_keypair_file(&outfile).unwrap().to_bytes().to_vec()
);
assert_eq!(
read_keypair(&outfile).unwrap().pubkey().as_ref().len(),
read_keypair_file(&outfile).unwrap().pubkey().as_ref().len(),
mem::size_of::<Pubkey>()
);
fs::remove_file(&outfile).unwrap();

View File

@ -14,7 +14,7 @@ use solana_core::socketaddr;
use solana_core::validator::{Validator, ValidatorConfig};
use solana_sdk::clock::Slot;
use solana_sdk::hash::Hash;
use solana_sdk::signature::{read_keypair, Keypair, KeypairUtil};
use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil};
use std::fs::{self, File};
use std::io::{self, Read};
use std::net::{SocketAddr, TcpListener};
@ -219,7 +219,7 @@ fn initialize_ledger_path(
// Return an error if a keypair file cannot be parsed.
fn is_keypair(string: String) -> Result<(), String> {
read_keypair(&string)
read_keypair_file(&string)
.map(|_| ())
.map_err(|err| format!("{:?}", err))
}
@ -419,7 +419,7 @@ pub fn main() {
let mut validator_config = ValidatorConfig::default();
let keypair = if let Some(identity) = matches.value_of("identity") {
read_keypair(identity).unwrap_or_else(|err| {
read_keypair_file(identity).unwrap_or_else(|err| {
error!("{}: Unable to open keypair file: {}", err, identity);
exit(1);
})
@ -428,7 +428,7 @@ pub fn main() {
};
let voting_keypair = if let Some(identity) = matches.value_of("voting_keypair") {
read_keypair(identity).unwrap_or_else(|err| {
read_keypair_file(identity).unwrap_or_else(|err| {
error!("{}: Unable to open keypair file: {}", err, identity);
exit(1);
})
@ -436,7 +436,7 @@ pub fn main() {
Keypair::new()
};
let storage_keypair = if let Some(storage_keypair) = matches.value_of("storage_keypair") {
read_keypair(storage_keypair).unwrap_or_else(|err| {
read_keypair_file(storage_keypair).unwrap_or_else(|err| {
error!("{}: Unable to open keypair file: {}", err, storage_keypair);
exit(1);
})