use crate::keychain::{KeychainKey, KeychainKeyValue}; use crate::{cmdutil, ecdsautil, hmacutil}; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; use std::collections::BTreeMap; pub struct CommandImpl; impl Command for CommandImpl { fn name(&self) -> &str { "keypair-generate" } fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()) .about("Generate software keypair") .arg( Arg::with_name("type") .long("type") .required(true) .takes_value(true) .help("Key type (e.g. p256, p384)"), ) .arg( Arg::with_name("with-hmac-encrypt") .long("with-hmac-encrypt") .help("With HMAC encrypt"), ) .arg(cmdutil::build_keychain_name_arg()) .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { let json_output = cmdutil::check_json_output(sub_arg_matches); let with_hmac_encrypt = sub_arg_matches.is_present("with-hmac-encrypt"); let key_type = sub_arg_matches.value_of("type").unwrap().to_lowercase(); let keychain_name = sub_arg_matches.value_of("keychain-name"); if let Some(keychain_name) = keychain_name { let keychain_key = KeychainKey::from_key_name_default(keychain_name); if let Some(_) = keychain_key.get_password()? { return simple_error!("Keychain key URI: {} exists", keychain_key.to_key_uri()); } } let (pkcs8_base64, secret_key_pem, public_key_pem, jwk_ec_key) = match key_type.as_str() { "p256" => ecdsautil::generate_p256_keypair()?, "p384" => ecdsautil::generate_p384_keypair()?, _ => { return simple_error!("Key type must be p256 or p384"); } }; let (pkcs8_base64, secret_key_pem) = if with_hmac_encrypt { ( hmacutil::hmac_encrypt_from_string(&pkcs8_base64)?, hmacutil::hmac_encrypt_from_string(&secret_key_pem)?, ) } else { (pkcs8_base64, secret_key_pem) }; let keychain_key_uri = if let Some(keychain_name) = keychain_name { let keychain_key_value = KeychainKeyValue { keychain_name: keychain_name.to_string(), pkcs8_base64: pkcs8_base64.clone(), secret_key_pem: secret_key_pem.clone(), public_key_pem: public_key_pem.clone(), public_key_jwk: jwk_ec_key.to_string(), }; let keychain_key_value_json = serde_json::to_string(&keychain_key_value)?; let keychain_key = KeychainKey::from_key_name_default(keychain_name); keychain_key.set_password(keychain_key_value_json.as_bytes())?; Some(keychain_key.to_key_uri()) } else { None }; if json_output { let mut json = BTreeMap::<&'_ str, String>::new(); match keychain_key_uri { None => { json.insert("private_key_base64", pkcs8_base64); json.insert("private_key_pem", secret_key_pem); } Some(keychain_key_uri) => { json.insert("keychain_key_uri", keychain_key_uri); } } json.insert("public_key_pem", public_key_pem); json.insert("public_key_jwk", jwk_ec_key.to_string()); println!("{}", serde_json::to_string_pretty(&json).unwrap()); } else { match keychain_key_uri { None => { information!("Private key base64:\n{}\n", pkcs8_base64); information!("Private key PEM:\n{}\n", secret_key_pem); } Some(keychain_key_uri) => { information!("Keychain key URI:\n{}\n", keychain_key_uri); } } information!("Public key PEM:\n{}", public_key_pem); information!("Public key JWK:\n{}", jwk_ec_key.to_string()); } Ok(None) } }