From 3a40d7f0ad12b090a6ac7fa4536723de7fece406 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Fri, 28 Mar 2025 07:35:53 +0800 Subject: [PATCH] feat: updates --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cmd_chall.rs | 8 +-- src/cmd_ec_verify.rs | 8 +-- src/cmd_file_sign.rs | 7 +- src/cmd_hmac_decrypt.rs | 10 +-- src/cmd_hmac_encrypt.rs | 10 +-- src/cmd_hmac_sha1.rs | 9 ++- src/cmd_keypair_generate.rs | 19 ++--- src/cmd_keypair_keychain_export.rs | 10 +-- src/cmd_keypair_keychain_import.rs | 8 +-- src/cmd_list.rs | 9 ++- src/cmd_parseecdsasignature.rs | 10 +-- src/cmd_pgp.rs | 2 +- src/cmd_pgp_age_address.rs | 9 ++- src/cmd_pgp_card_decrypt.rs | 9 ++- src/cmd_pgp_card_list.rs | 9 ++- src/cmd_pgp_card_sign.rs | 9 ++- src/cmd_piv_decrypt.rs | 12 ++-- src/cmd_piv_ecdh.rs | 12 ++-- src/cmd_piv_ecsign.rs | 12 ++-- src/cmd_piv_generate.rs | 10 +-- src/cmd_piv_meta.rs | 11 ++- src/cmd_piv_rsasign.rs | 12 ++-- src/cmd_piv_summary.rs | 10 +-- src/cmd_piv_verify.rs | 11 ++- src/cmd_rsa_decrypt.rs | 7 +- src/cmd_rsa_encrypt.rs | 8 +-- src/cmd_se.rs | 23 ++++-- src/cmd_se_ecdh.rs | 12 ++-- src/cmd_se_ecsign.rs | 11 ++- src/cmd_se_generate.rs | 11 ++- src/cmd_se_recover.rs | 12 ++-- src/cmd_sign_jwt.rs | 112 ++++++++++++++++++++--------- src/cmd_sign_jwt_se.rs | 19 ++--- src/cmd_sign_jwt_soft.rs | 19 ++--- src/cmd_ssh_piv_cert.rs | 6 +- src/cmd_ssh_piv_sign.rs | 6 +- src/cmd_ssh_pub_key.rs | 78 ++++++++++++++------ src/cmd_u2f_register.rs | 8 +-- src/cmd_u2f_sign.rs | 7 +- src/cmdutil.rs | 33 +++++++++ src/main.rs | 1 + 43 files changed, 324 insertions(+), 289 deletions(-) create mode 100644 src/cmdutil.rs diff --git a/Cargo.lock b/Cargo.lock index 2ebd23c..934c6d7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,7 +508,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.11.10" +version = "1.11.11" dependencies = [ "aes-gcm-stream", "authenticator 0.3.1", diff --git a/Cargo.toml b/Cargo.toml index af527e3..6e4ad3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.11.10" +version = "1.11.11" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_chall.rs b/src/cmd_chall.rs index 52ee849..c249047 100644 --- a/src/cmd_chall.rs +++ b/src/cmd_chall.rs @@ -1,8 +1,7 @@ use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; -use crate::hmacutil; +use crate::{cmdutil, hmacutil}; pub struct CommandImpl; @@ -17,12 +16,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("sha256").short("2").long("sha256").help("Output SHA256")) .arg(Arg::with_name("sha384").short("3").long("sha384").help("Output SHA384")) .arg(Arg::with_name("sha512").short("5").long("sha512").help("Output SHA512")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let challenge_bytes = hmacutil::get_challenge_bytes(sub_arg_matches)?; let hmac_result = hmacutil::compute_yubikey_hmac(&challenge_bytes)?; diff --git a/src/cmd_ec_verify.rs b/src/cmd_ec_verify.rs index 8c63a43..0a0b5c7 100644 --- a/src/cmd_ec_verify.rs +++ b/src/cmd_ec_verify.rs @@ -2,10 +2,9 @@ use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use crate::ecdsautil::EcdsaAlgorithm; -use crate::{argsutil, ecdsautil}; +use crate::{argsutil, cmdutil, ecdsautil}; pub struct CommandImpl; @@ -19,12 +18,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("file").short("f").long("file").takes_value(true).help("Input file")) .arg(Arg::with_name("input").short("i").long("input").takes_value(true).help("Input")) .arg(Arg::with_name("hash-hex").short("x").long("hash-hex").takes_value(true).help("Hash")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let hash_bytes = argsutil::get_sha256_digest_or_hash(sub_arg_matches)?; let public_key = if let Some(public_key_hex) = sub_arg_matches.value_of("public-key-hex") { diff --git a/src/cmd_file_sign.rs b/src/cmd_file_sign.rs index e15e645..60daf91 100644 --- a/src/cmd_file_sign.rs +++ b/src/cmd_file_sign.rs @@ -10,7 +10,7 @@ use x509_parser::nom::AsBytes; use yubikey::{Key, YubiKey}; use yubikey::piv::{sign_data, SlotId}; -use crate::{argsutil, pinutil, pivutil}; +use crate::{argsutil, cmdutil, pinutil, pivutil}; use crate::digest::sha256_bytes; use crate::signfile::{CERTIFICATES_SEARCH_URL, HASH_ALGORITHM_SHA256, SIGNATURE_ALGORITHM_SHA256_WITH_ECDSA, SignFileRequest, SIMPLE_SIG_SCHEMA, SimpleSignFile, SimpleSignFileSignature}; use crate::util::base64_encode; @@ -40,10 +40,9 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("PIV sign(with SHA256) subcommand") - .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")) + .arg(cmdutil::build_slot_arg()) + .arg(cmdutil::build_pin_arg()) .arg(Arg::with_name("no-pin").long("no-pin").help("No PIN")) - .arg(Arg::with_name("slot").short("s").long("slot") - .takes_value(true).required(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) .arg(Arg::with_name("file").short("f").long("file").takes_value(true).required(true).help("Input file")) .arg(Arg::with_name("filename").short("n").long("filename").takes_value(true).help("Filename")) .arg(Arg::with_name("sign-file").short("S").long("sign-file").takes_value(false).help("Sign file")) diff --git a/src/cmd_hmac_decrypt.rs b/src/cmd_hmac_decrypt.rs index d702294..95fecb8 100644 --- a/src/cmd_hmac_decrypt.rs +++ b/src/cmd_hmac_decrypt.rs @@ -1,9 +1,8 @@ use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use std::collections::BTreeMap; -use crate::hmacutil; +use crate::{cmdutil, hmacutil}; pub struct CommandImpl; @@ -21,14 +20,11 @@ impl Command for CommandImpl { .takes_value(true) .help("Ciphertext"), ) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } + let json_output = cmdutil::check_json_output(sub_arg_matches); let ciphertext = sub_arg_matches.value_of("ciphertext").unwrap(); let plaintext = hmacutil::hmac_decrypt_to_string(ciphertext)?; diff --git a/src/cmd_hmac_encrypt.rs b/src/cmd_hmac_encrypt.rs index d5f1bb3..2cfa911 100644 --- a/src/cmd_hmac_encrypt.rs +++ b/src/cmd_hmac_encrypt.rs @@ -1,9 +1,8 @@ use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use std::collections::BTreeMap; -use crate::hmacutil; +use crate::{cmdutil, hmacutil}; pub struct CommandImpl; @@ -21,14 +20,11 @@ impl Command for CommandImpl { .takes_value(true) .help("Plaintext"), ) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } + let json_output = cmdutil::check_json_output(sub_arg_matches); let plaintext = sub_arg_matches.value_of("plaintext").unwrap(); let hmac_encrypt_ciphertext = hmacutil::hmac_encrypt_from_string(plaintext)?; diff --git a/src/cmd_hmac_sha1.rs b/src/cmd_hmac_sha1.rs index 6a9213a..a95d00e 100644 --- a/src/cmd_hmac_sha1.rs +++ b/src/cmd_hmac_sha1.rs @@ -1,8 +1,8 @@ use clap::{App, Arg, ArgMatches, SubCommand}; -use rust_util::{util_msg, XResult}; +use rust_util::XResult; use rust_util::util_clap::{Command, CommandError}; -use crate::hmacutil; +use crate::{cmdutil, hmacutil}; pub struct CommandImpl; @@ -19,12 +19,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("sha256").short("2").long("sha256").help("Output SHA256")) .arg(Arg::with_name("sha384").short("3").long("sha384").help("Output SHA256")) .arg(Arg::with_name("sha512").short("5").long("sha512").help("Output SHA256")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let variable = sub_arg_matches.is_present("variable"); let secret_bytes = get_secret_bytes(sub_arg_matches)?; diff --git a/src/cmd_keypair_generate.rs b/src/cmd_keypair_generate.rs index 716fb56..aa94a70 100644 --- a/src/cmd_keypair_generate.rs +++ b/src/cmd_keypair_generate.rs @@ -1,8 +1,7 @@ use crate::keychain::{KeychainKey, KeychainKeyValue}; -use crate::{ecdsautil, hmacutil}; +use crate::{cmdutil, ecdsautil, hmacutil}; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use std::collections::BTreeMap; pub struct CommandImpl; @@ -27,16 +26,13 @@ impl Command for CommandImpl { .long("with-hmac-encrypt") .help("With HMAC encrypt"), ) - .arg( - Arg::with_name("keychain-name") - .long("keychain-name") - .takes_value(true) - .help("Key chain name"), - ) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .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"); @@ -48,11 +44,6 @@ impl Command for CommandImpl { } } - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } - 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()?, diff --git a/src/cmd_keypair_keychain_export.rs b/src/cmd_keypair_keychain_export.rs index 34bbcd7..8c79c39 100644 --- a/src/cmd_keypair_keychain_export.rs +++ b/src/cmd_keypair_keychain_export.rs @@ -1,5 +1,6 @@ +use crate::cmdutil; use crate::keychain::{KeychainKey, KeychainKeyValue}; -use clap::{App, Arg, ArgMatches, SubCommand}; +use clap::{App, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; use rust_util::util_msg; @@ -13,12 +14,7 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()) .about("Export software keypair from keychain") - .arg( - Arg::with_name("keychain-name") - .long("keychain-name") - .takes_value(true) - .help("Key chain name"), - ) + .arg(cmdutil::build_keychain_name_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { diff --git a/src/cmd_keypair_keychain_import.rs b/src/cmd_keypair_keychain_import.rs index a9076fa..43c253a 100644 --- a/src/cmd_keypair_keychain_import.rs +++ b/src/cmd_keypair_keychain_import.rs @@ -1,6 +1,7 @@ use crate::keychain::KeychainKey; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; +use crate::cmdutil; pub struct CommandImpl; @@ -12,12 +13,7 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()) .about("Import software keypair to keychain") - .arg( - Arg::with_name("keychain-name") - .long("keychain-name") - .takes_value(true) - .help("Key chain name"), - ) + .arg(cmdutil::build_keychain_name_arg()) .arg( Arg::with_name("import-key-value") .long("import-key-value") diff --git a/src/cmd_list.rs b/src/cmd_list.rs index 3332250..f8fd126 100644 --- a/src/cmd_list.rs +++ b/src/cmd_list.rs @@ -1,9 +1,9 @@ use std::collections::BTreeMap; -use clap::{App, Arg, ArgMatches, SubCommand}; +use clap::{App, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use yubikey::YubiKey; +use crate::cmdutil; pub struct CommandImpl; @@ -12,12 +12,11 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("YubiKey list") - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}"); diff --git a/src/cmd_parseecdsasignature.rs b/src/cmd_parseecdsasignature.rs index 927b27e..32d6961 100644 --- a/src/cmd_parseecdsasignature.rs +++ b/src/cmd_parseecdsasignature.rs @@ -3,8 +3,7 @@ use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; - +use crate::cmdutil; use crate::ecdsautil::parse_ecdsa_r_and_s; use crate::util::try_decode; @@ -25,14 +24,11 @@ impl Command for CommandImpl { .takes_value(true) .help("ECDSA signature"), ) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } + let json_output = cmdutil::check_json_output(sub_arg_matches); let mut json = BTreeMap::<&'_ str, String>::new(); diff --git a/src/cmd_pgp.rs b/src/cmd_pgp.rs index a362465..49fd3b9 100644 --- a/src/cmd_pgp.rs +++ b/src/cmd_pgp.rs @@ -21,7 +21,7 @@ impl Command for CommandImpl { .arg(Arg::with_name("in").short("i").long("in").takes_value(true).help("File input, *.pgp or *.asc")) .arg(Arg::with_name("detail").long("detail").help("Detail output")) .arg(Arg::with_name("verbose").long("verbose").help("Verbose output")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + // .arg(Arg::with_name("json").long("json").help("JSON output")) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { diff --git a/src/cmd_pgp_age_address.rs b/src/cmd_pgp_age_address.rs index 6149d14..e267fe8 100644 --- a/src/cmd_pgp_age_address.rs +++ b/src/cmd_pgp_age_address.rs @@ -1,12 +1,12 @@ use bech32::{ToBase32, Variant}; -use clap::{App, Arg, ArgMatches, SubCommand}; +use clap::{App, ArgMatches, SubCommand}; use openpgp_card::algorithm::{Algo, Curve}; use openpgp_card::crypto_data::{EccType, PublicKeyMaterial}; use openpgp_card::{KeyType, OpenPgp}; use openpgp_card_pcsc::PcscBackend; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use std::collections::BTreeMap; +use crate::cmdutil; const AGE_PUBLIC_KEY_PREFIX: &str = "age"; @@ -17,12 +17,11 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("OpenPGP Card encryption key to age address") - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let cards = opt_result!(PcscBackend::cards(None), "Failed to list OpenPGP cards: {}"); diff --git a/src/cmd_pgp_card_decrypt.rs b/src/cmd_pgp_card_decrypt.rs index eeb3197..a795aa0 100644 --- a/src/cmd_pgp_card_decrypt.rs +++ b/src/cmd_pgp_card_decrypt.rs @@ -2,10 +2,10 @@ use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; use openpgp_card::crypto_data::Cryptogram; -use rust_util::{util_msg, XResult}; +use rust_util::XResult; use rust_util::util_clap::{Command, CommandError}; -use crate::{pgpcardutil, pinutil}; +use crate::{cmdutil, pgpcardutil, pinutil}; use crate::util::{base64_encode, read_stdin, try_decode}; #[derive(Debug, Clone, Copy)] @@ -36,12 +36,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("ciphertext").short("c").long("ciphertext").takes_value(true).help("Cipher text (HEX or Base64)")) .arg(Arg::with_name("stdin").long("stdin").help("Standard input (Ciphertext)")) .arg(Arg::with_name("algo").long("algo").takes_value(true).help("Algo: RSA, X25519/ECDH")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let pin_opt = sub_arg_matches.value_of("pass").or_else(|| sub_arg_matches.value_of("pin")); let pin_opt = pinutil::get_pin(pin_opt); diff --git a/src/cmd_pgp_card_list.rs b/src/cmd_pgp_card_list.rs index 233ca52..4d2f168 100644 --- a/src/cmd_pgp_card_list.rs +++ b/src/cmd_pgp_card_list.rs @@ -4,8 +4,7 @@ use clap::{App, Arg, ArgMatches, SubCommand}; use openpgp_card::{KeyType, OpenPgp}; use openpgp_card_pcsc::PcscBackend; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; - +use crate::cmdutil; use crate::pkiutil::openpgp_card_public_key_pem as public_key_pem; pub struct CommandImpl; @@ -16,13 +15,13 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("OpenPGP Card list subcommand") .arg(Arg::with_name("detail").long("detail").help("Detail output")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .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 detail_output = sub_arg_matches.is_present("detail"); - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } let mut jsons = vec![]; let cards = opt_result!(PcscBackend::cards(None), "Failed to list OpenPGP cards: {}"); diff --git a/src/cmd_pgp_card_sign.rs b/src/cmd_pgp_card_sign.rs index a1ebffb..95d3dc9 100644 --- a/src/cmd_pgp_card_sign.rs +++ b/src/cmd_pgp_card_sign.rs @@ -5,11 +5,11 @@ use std::io::{ErrorKind, Read}; use clap::{App, Arg, ArgMatches, SubCommand}; use digest::Digest; use openpgp_card::crypto_data::Hash; -use rust_util::{util_msg, XResult}; +use rust_util::XResult; use rust_util::util_clap::{Command, CommandError}; use sha2::{Sha256, Sha384, Sha512}; -use crate::{pgpcardutil, pinutil}; +use crate::{cmdutil, pgpcardutil, pinutil}; use crate::util::base64_encode; const BUFF_SIZE: usize = 512 * 1024; @@ -49,12 +49,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("use-sha384").long("use-sha384").help("Use SHA384 for file in")) .arg(Arg::with_name("use-sha512").long("use-sha512").help("Use SHA512 for file in")) .arg(Arg::with_name("algo").long("algo").takes_value(true).help("Algorithm, rsa, ecdsa, eddsa")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let pin_opt = sub_arg_matches.value_of("pass").or_else(|| sub_arg_matches.value_of("pin")); let pin_opt = pinutil::get_pin(pin_opt); diff --git a/src/cmd_piv_decrypt.rs b/src/cmd_piv_decrypt.rs index 19a0a13..395e2e9 100644 --- a/src/cmd_piv_decrypt.rs +++ b/src/cmd_piv_decrypt.rs @@ -2,11 +2,10 @@ use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use yubikey::piv::AlgorithmId; use yubikey::YubiKey; -use crate::{pinutil, pivutil}; +use crate::{cmdutil, pinutil, pivutil}; use crate::util::{read_stdin, try_decode}; pub struct CommandImpl; @@ -16,17 +15,16 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("PIV decrypt(RSA) subcommand") - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) - .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")) + .arg(cmdutil::build_slot_arg()) + .arg(cmdutil::build_pin_arg()) .arg(Arg::with_name("no-pin").long("no-pin").help("No PIN")) .arg(Arg::with_name("ciphertext").long("ciphertext").short("c").takes_value(true).help("Encrypted data (HEX or Base64)")) .arg(Arg::with_name("stdin").long("stdin").help("Standard input (Ciphertext)")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let slot = opt_value_result!(sub_arg_matches.value_of("slot"), "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"); diff --git a/src/cmd_piv_ecdh.rs b/src/cmd_piv_ecdh.rs index 0e7a1ec..e8f6351 100644 --- a/src/cmd_piv_ecdh.rs +++ b/src/cmd_piv_ecdh.rs @@ -4,11 +4,10 @@ use std::fs; use clap::{App, Arg, ArgMatches, SubCommand}; use rand::rngs::OsRng; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use yubikey::{PinPolicy, YubiKey}; use yubikey::piv::{AlgorithmId, decrypt_data, metadata}; -use crate::{ecdhutil, pinutil, pivutil}; +use crate::{cmdutil, ecdhutil, pinutil, pivutil}; use crate::pivutil::get_algorithm_id; pub struct CommandImpl; @@ -18,9 +17,9 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("PIV ECDH subcommand") - .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")) + .arg(cmdutil::build_pin_arg()) .arg(Arg::with_name("no-pin").long("no-pin").help("No PIN")) - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ...")) + .arg(cmdutil::build_slot_arg()) .arg(Arg::with_name("public-256").long("public-256").help("Public key (P-256)")) .arg(Arg::with_name("public-384").long("public-384").help("Public key (P-384)")) .arg(Arg::with_name("private").long("private").help("Private key(PIV)")) @@ -28,12 +27,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("public-key").long("public-key").takes_value(true).help("Public key")) .arg(Arg::with_name("public-key-file").long("public-key-file").takes_value(true).help("Public key")) .arg(Arg::with_name("public-key-point-hex").long("public-key-point-hex").takes_value(true).help("Public key point hex")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let public256 = sub_arg_matches.is_present("public-256"); let public384 = sub_arg_matches.is_present("public-384"); diff --git a/src/cmd_piv_ecsign.rs b/src/cmd_piv_ecsign.rs index fabff4d..bb5ac7b 100644 --- a/src/cmd_piv_ecsign.rs +++ b/src/cmd_piv_ecsign.rs @@ -2,13 +2,12 @@ use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use x509_parser::nom::AsBytes; use yubikey::piv::{metadata, sign_data, AlgorithmId, ManagementAlgorithmId}; use yubikey::YubiKey; use crate::util::base64_encode; -use crate::{argsutil, pivutil}; +use crate::{argsutil, cmdutil, pivutil}; pub struct CommandImpl; @@ -17,19 +16,18 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("PIV EC sign(with SHA256) subcommand") - .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")) + .arg(cmdutil::build_slot_arg()) + .arg(cmdutil::build_pin_arg()) .arg(Arg::with_name("no-pin").long("no-pin").help("No PIN")) - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) .arg(Arg::with_name("algorithm").short("a").long("algorithm").takes_value(true).help("Algorithm, p256 or p384")) .arg(Arg::with_name("file").short("f").long("file").takes_value(true).help("Input file")) .arg(Arg::with_name("input").short("i").long("input").takes_value(true).help("Input")) .arg(Arg::with_name("hash-hex").short("x").long("hash-hex").takes_value(true).help("Hash")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let mut json = BTreeMap::<&'_ str, String>::new(); diff --git a/src/cmd_piv_generate.rs b/src/cmd_piv_generate.rs index b6f42c8..a6a9e41 100644 --- a/src/cmd_piv_generate.rs +++ b/src/cmd_piv_generate.rs @@ -1,10 +1,9 @@ use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use yubikey::{PinPolicy, piv, TouchPolicy, YubiKey}; use yubikey::piv::{AlgorithmId, SlotId}; -use crate::pinutil; +use crate::{cmdutil, pinutil}; pub struct CommandImpl; @@ -13,15 +12,12 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("PIV generate subcommand") - .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")) + .arg(cmdutil::build_pin_arg()) .arg(Arg::with_name("force").long("force").help("Force generate")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + // .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } - warning!("This feature is not works"); let pin_opt = sub_arg_matches.value_of("pin"); let pin_opt = pinutil::get_pin(pin_opt); diff --git a/src/cmd_piv_meta.rs b/src/cmd_piv_meta.rs index b0b671c..1ddf121 100644 --- a/src/cmd_piv_meta.rs +++ b/src/cmd_piv_meta.rs @@ -1,6 +1,6 @@ use std::collections::BTreeMap; -use clap::{App, Arg, ArgMatches, SubCommand}; +use clap::{App, ArgMatches, SubCommand}; use p256::pkcs8::der::Encode; use rust_util::util_clap::{Command, CommandError}; use rust_util::util_msg; @@ -9,7 +9,7 @@ use x509_parser::parse_x509_certificate; use yubikey::{Key, YubiKey}; use yubikey::piv::{AlgorithmId, metadata}; -use crate::pivutil; +use crate::{cmdutil, pivutil}; use crate::pivutil::{get_algorithm_id_by_certificate, slot_equals, ToStr}; use crate::pkiutil::bytes_to_pem; use crate::sshutil::SshVecWriter; @@ -22,13 +22,12 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("PIV meta subcommand") - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_slot_arg()) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let mut json = BTreeMap::<&'_ str, String>::new(); diff --git a/src/cmd_piv_rsasign.rs b/src/cmd_piv_rsasign.rs index 65720aa..589c3a4 100644 --- a/src/cmd_piv_rsasign.rs +++ b/src/cmd_piv_rsasign.rs @@ -2,11 +2,10 @@ use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use yubikey::{piv, YubiKey}; use yubikey::piv::{AlgorithmId, SlotId}; -use crate::{pinutil, pivutil, rsautil}; +use crate::{cmdutil, pinutil, pivutil, rsautil}; use crate::util::base64_encode; pub struct CommandImpl; @@ -16,16 +15,15 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("PIV RSA sign(with SHA256) subcommand") - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) - .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")) + .arg(cmdutil::build_slot_arg()) + .arg(cmdutil::build_pin_arg()) .arg(Arg::with_name("no-pin").long("no-pin").help("No PIN")) .arg(Arg::with_name("sha256").short("2").long("sha256").takes_value(true).help("Digest SHA256 HEX")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let pin_opt = pinutil::read_pin(sub_arg_matches); diff --git a/src/cmd_piv_summary.rs b/src/cmd_piv_summary.rs index 20c3907..38897a4 100644 --- a/src/cmd_piv_summary.rs +++ b/src/cmd_piv_summary.rs @@ -1,6 +1,6 @@ use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::{util_msg, XResult}; +use rust_util::XResult; use serde::Serialize; use serde_json::{Map, Value}; use spki::der::Encode; @@ -9,7 +9,7 @@ use tabled::{Table, Tabled}; use x509_parser::parse_x509_certificate; use yubikey::piv::{metadata, SlotId}; use yubikey::{Certificate, YubiKey}; - +use crate::cmdutil; use crate::pivutil::{get_algorithm_id_by_certificate, ToStr, ORDERED_SLOTS}; const NA: &str = "N/A"; @@ -35,17 +35,17 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("PIV subcommand") .arg(Arg::with_name("table").long("table").help("Show table")) - .arg(Arg::with_name("json").long("json").help("JSON output")) .arg(Arg::with_name("all").long("all").help("Show all")) .arg(Arg::with_name("ordered").long("ordered").help("Show ordered")) + .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 show_table = sub_arg_matches.is_present("table"); let show_all = sub_arg_matches.is_present("all"); let show_ordered = sub_arg_matches.is_present("ordered"); - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } let mut output = Map::new(); let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}"); diff --git a/src/cmd_piv_verify.rs b/src/cmd_piv_verify.rs index d73e21a..a284029 100644 --- a/src/cmd_piv_verify.rs +++ b/src/cmd_piv_verify.rs @@ -2,12 +2,12 @@ use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; use openssl::rsa::{Padding, Rsa}; -use rust_util::{util_msg, XResult}; +use rust_util::XResult; use rust_util::util_clap::{Command, CommandError}; use yubikey::{Key, YubiKey}; use yubikey::piv::{AlgorithmId, SlotId}; -use crate::{argsutil, ecdsautil, pivutil}; +use crate::{argsutil, cmdutil, ecdsautil, pivutil}; use crate::ecdsautil::EcdsaAlgorithm; use crate::pivutil::slot_equals; @@ -18,17 +18,16 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("PIV verify subcommand") - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) + .arg(cmdutil::build_slot_arg()) .arg(Arg::with_name("signature-hex").short("t").long("signature-hex").takes_value(true).help("Signature")) .arg(Arg::with_name("file").short("f").long("file").takes_value(true).help("Input file")) .arg(Arg::with_name("input").short("i").long("input").takes_value(true).help("Input")) .arg(Arg::with_name("hash-hex").short("x").long("hash-hex").takes_value(true).help("Hash")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let hash_bytes = argsutil::get_sha256_digest_or_hash(sub_arg_matches)?; let signature = if let Some(signature_hex) = sub_arg_matches.value_of("signature-hex") { diff --git a/src/cmd_rsa_decrypt.rs b/src/cmd_rsa_decrypt.rs index b309b9f..c323002 100644 --- a/src/cmd_rsa_decrypt.rs +++ b/src/cmd_rsa_decrypt.rs @@ -8,7 +8,7 @@ use openssl::rsa::Rsa; use rust_util::util_clap::{Command, CommandError}; use rust_util::util_msg; use rust_util::util_msg::MessageType; - +use crate::cmdutil; use crate::util::{read_stdin, try_decode}; pub struct CommandImpl; @@ -25,12 +25,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("stdin").long("stdin").help("Standard input (Ciphertext)")) .arg(Arg::with_name("padding").long("padding").takes_value(true) .possible_values(&["pkcs1", "oaep", "pss", "none"]).help("Padding")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let pri_key_in = opt_value_result!(sub_arg_matches.value_of("pri-key-in"), "Require private key in"); let pri_key_bytes = opt_result!(std::fs::read(pri_key_in), "Read file: {}, failed: {}", pri_key_in); diff --git a/src/cmd_rsa_encrypt.rs b/src/cmd_rsa_encrypt.rs index eaa5239..2486c6e 100644 --- a/src/cmd_rsa_encrypt.rs +++ b/src/cmd_rsa_encrypt.rs @@ -6,8 +6,7 @@ use openssl::encrypt::Encrypter; use openssl::pkey::PKey; use openssl::rsa::Rsa; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; - +use crate::cmdutil; use crate::digest::sha256_bytes; pub struct CommandImpl; @@ -23,12 +22,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("data-hex").long("data-hex").takes_value(true).help("Data in HEX")) .arg(Arg::with_name("padding").long("padding").takes_value(true) .possible_values(&["pkcs1", "oaep", "pss", "none"]).help("Padding")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let pub_key_in = opt_value_result!(sub_arg_matches.value_of("pub-key-in"), "Require public key in"); let pub_key_bytes = opt_result!(fs::read(pub_key_in), "Read file: {}, failed: {}", pub_key_in); diff --git a/src/cmd_se.rs b/src/cmd_se.rs index 61aee35..a1340fa 100644 --- a/src/cmd_se.rs +++ b/src/cmd_se.rs @@ -1,4 +1,5 @@ -use crate::seutil; +use std::collections::BTreeMap; +use crate::{cmdutil, seutil}; use clap::{App, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; @@ -11,15 +12,25 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("Secure Enclave subcommand") - // .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } - fn run(&self, _arg_matches: &ArgMatches, _sub_arg_matches: &ArgMatches) -> CommandError { - if seutil::is_support_se() { - success!("Secure Enclave is supported.") + fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { + let json_output = cmdutil::check_json_output(sub_arg_matches); + + let mut json = BTreeMap::new(); + + if json_output { + json.insert("se_supported", seutil::is_support_se()); } else { - failure!("Secure Enclave is NOT supported.") + if seutil::is_support_se() { + success!("Secure Enclave is supported.") + } else { + failure!("Secure Enclave is NOT supported.") + } } + + if json_output {} Ok(None) } } diff --git a/src/cmd_se_ecdh.rs b/src/cmd_se_ecdh.rs index 449d84c..9945c07 100644 --- a/src/cmd_se_ecdh.rs +++ b/src/cmd_se_ecdh.rs @@ -1,10 +1,9 @@ use crate::keyutil::{parse_key_uri, KeyUri}; -use crate::seutil; +use crate::{cmdutil, seutil}; use clap::{App, Arg, ArgMatches, SubCommand}; use p256::elliptic_curve::sec1::FromEncodedPoint; use p256::{EncodedPoint, PublicKey}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use spki::EncodePublicKey; use std::collections::BTreeMap; @@ -32,21 +31,18 @@ impl Command for CommandImpl { .takes_value(true) .help("E-Public key"), ) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .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); + if !seutil::is_support_se() { return simple_error!("Secure Enclave is NOT supported."); } let key = sub_arg_matches.value_of("key").unwrap(); let epk = sub_arg_matches.value_of("epk").unwrap(); - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } - let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?; debugging!("Secure enclave key URI: {:?}", se_key_uri); diff --git a/src/cmd_se_ecsign.rs b/src/cmd_se_ecsign.rs index 31d9271..3a8cd6b 100644 --- a/src/cmd_se_ecsign.rs +++ b/src/cmd_se_ecsign.rs @@ -1,9 +1,8 @@ use crate::keyutil::{parse_key_uri, KeyUri}; -use crate::seutil; +use crate::{cmdutil, seutil}; use crate::util::{base64_decode, base64_encode}; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use std::collections::BTreeMap; pub struct CommandImpl; @@ -36,10 +35,12 @@ impl Command for CommandImpl { .takes_value(true) .help("Input in base64"), ) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .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); + if !seutil::is_support_se() { return simple_error!("Secure Enclave is NOT supported."); } @@ -51,10 +52,6 @@ impl Command for CommandImpl { }, Some(input) => input.as_bytes().to_vec(), }; - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?; debugging!("Secure enclave key URI: {:?}", se_key_uri); diff --git a/src/cmd_se_generate.rs b/src/cmd_se_generate.rs index 3d7091c..60376ab 100644 --- a/src/cmd_se_generate.rs +++ b/src/cmd_se_generate.rs @@ -1,10 +1,9 @@ use crate::pkiutil::bytes_to_pem; -use crate::seutil; +use crate::{cmdutil, seutil}; use crate::util::base64_encode; use clap::{App, Arg, ArgMatches, SubCommand}; use p256::PublicKey; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use spki::DecodePublicKey; use std::collections::BTreeMap; @@ -37,19 +36,17 @@ impl Command for CommandImpl { .long("disable-bio") .help("Disable bio"), ) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .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); + if !seutil::is_support_se() { return simple_error!("Secure Enclave is NOT supported."); } let ty = sub_arg_matches.value_of("type").unwrap(); let host = sub_arg_matches.value_of("host").unwrap_or("macbook"); - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } let sign = match ty { "signing" | "ecsign" | "sign" => true, diff --git a/src/cmd_se_recover.rs b/src/cmd_se_recover.rs index 12dbe2d..a2a116f 100644 --- a/src/cmd_se_recover.rs +++ b/src/cmd_se_recover.rs @@ -1,9 +1,8 @@ use crate::cmd_se_generate::print_se_key; use crate::keyutil::{parse_key_uri, KeyUri, KeyUsage}; -use crate::seutil; +use crate::{cmdutil, seutil}; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; pub struct CommandImpl; @@ -22,20 +21,17 @@ impl Command for CommandImpl { .takes_value(true) .help("Key uri"), ) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .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); + if !seutil::is_support_se() { return simple_error!("Secure Enclave is NOT supported."); } let key_uri = sub_arg_matches.value_of("key").unwrap(); - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } - let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key_uri)?; debugging!("Secure enclave key URI: {:?}", se_key_uri); diff --git a/src/cmd_sign_jwt.rs b/src/cmd_sign_jwt.rs index 90ee9d8..50c6274 100644 --- a/src/cmd_sign_jwt.rs +++ b/src/cmd_sign_jwt.rs @@ -2,45 +2,44 @@ use std::borrow::Cow; use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; -use jwt::{AlgorithmType, Header, ToBase64}; use jwt::header::HeaderType; -use rust_util::{util_msg, util_time, XResult}; +use jwt::{AlgorithmType, Header, ToBase64}; use rust_util::util_clap::{Command, CommandError}; +use rust_util::{util_time, XResult}; use serde_json::{Map, Number, Value}; +use yubikey::piv::{sign_data, AlgorithmId, SlotId}; use yubikey::{Certificate, YubiKey}; -use yubikey::piv::{AlgorithmId, sign_data, SlotId}; -use crate::{digest, pivutil, rsautil, util}; use crate::ecdsautil::parse_ecdsa_to_rs; +use crate::{cmdutil, digest, pivutil, rsautil, util}; const SEPARATOR: &str = "."; pub struct CommandImpl; impl Command for CommandImpl { - fn name(&self) -> &str { "sign-jwt" } + fn name(&self) -> &str { + "sign-jwt" + } fn subcommand<'a>(&self) -> App<'a, 'a> { - SubCommand::with_name(self.name()).about("Sign JWT subcommand") - .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")) + let app = SubCommand::with_name(self.name()).about("Sign JWT subcommand") + .arg(cmdutil::build_pin_arg()) .arg(Arg::with_name("no-pin").long("no-pin").help("No PIN")) - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) - .arg(Arg::with_name("key-id").short("K").long("key-id").takes_value(true).help("Header key ID")) - .arg(Arg::with_name("claims").short("C").long("claims").takes_value(true).multiple(true).help("Claims, key:value")) - .arg(Arg::with_name("payload").short("P").long("payload").takes_value(true).help("Claims in JSON")) - .arg(Arg::with_name("jti").long("jti").help("Claims jti")) - .arg(Arg::with_name("validity").long("validity").takes_value(true).help("Claims validity period e.g. 10m means 10 minutes (s - second, m - minute, h - hour, d - day)")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_slot_arg()) + .arg(cmdutil::build_json_arg()); + fill_sign_jwt_app_args(app) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let mut json = BTreeMap::<&'_ str, String>::new(); let slot = opt_value_result!( - sub_arg_matches.value_of("slot"), "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"); + sub_arg_matches.value_of("slot"), + "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e" + ); let (header, payload, jwt_claims) = build_jwt_parts(sub_arg_matches)?; @@ -64,15 +63,34 @@ impl Command for CommandImpl { } } -fn sign_jwt(yk: &mut YubiKey, slot_id: SlotId, pin_opt: &Option, mut header: Header, payload: &Option, claims: &Map) -> XResult { +pub fn fill_sign_jwt_app_args<'a>(app: App<'a, 'a>) -> App<'a, 'a> { + app.arg(Arg::with_name("key-id").short("K").long("key-id").takes_value(true).help("Header key ID")) + .arg(Arg::with_name("claims").short("C").long("claims").takes_value(true).multiple(true).help("Claims, key:value")) + .arg(Arg::with_name("payload").short("P").long("payload").takes_value(true).help("Claims in JSON")) + .arg(Arg::with_name("jti").long("jti").help("Claims jti")) + .arg(Arg::with_name("validity").long("validity").takes_value(true).help("Claims validity period e.g. 10m means 10 minutes (s - second, m - minute, h - hour, d - day)")) +} + +fn sign_jwt( + yk: &mut YubiKey, + slot_id: SlotId, + pin_opt: &Option, + mut header: Header, + payload: &Option, + claims: &Map, +) -> XResult { if let Some(pin) = pin_opt { - opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}"); + opt_result!( + yk.verify_pin(pin.as_bytes()), + "YubiKey verify pin failed: {}" + ); } let cert = match Certificate::read(yk, slot_id) { Ok(c) => c, Err(e) => return simple_error!("Read YubiKey certificate failed: {}", e), }; - let piv_algorithm_id = pivutil::get_algorithm_id(&cert.cert.tbs_certificate.subject_public_key_info)?; + let piv_algorithm_id = + pivutil::get_algorithm_id(&cert.cert.tbs_certificate.subject_public_key_info)?; let (jwt_algorithm, yk_algorithm) = match piv_algorithm_id { AlgorithmId::Rsa1024 => return simple_error!("RSA 1024 bits not supported."), @@ -90,18 +108,21 @@ fn sign_jwt(yk: &mut YubiKey, slot_id: SlotId, pin_opt: &Option, mut hea let tobe_signed = merge_header_claims(header.as_bytes(), claims.as_bytes()); let raw_in = match jwt_algorithm { - AlgorithmType::Rs256 => rsautil::pkcs15_sha256_rsa_2048_padding_for_sign( - &digest::sha256_bytes(&tobe_signed)), + AlgorithmType::Rs256 => { + rsautil::pkcs15_sha256_rsa_2048_padding_for_sign(&digest::sha256_bytes(&tobe_signed)) + } AlgorithmType::Es256 => digest::sha256_bytes(&tobe_signed), AlgorithmType::Es384 => digest::sha384_bytes(&tobe_signed), _ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm), }; let signed_data = opt_result!( - sign_data(yk, &raw_in, yk_algorithm, slot_id), "Sign YubiKey failed: {}"); + sign_data(yk, &raw_in, yk_algorithm, slot_id), + "Sign YubiKey failed: {}" + ); let signed_data = match jwt_algorithm { AlgorithmType::Rs256 => signed_data.to_vec(), - AlgorithmType::Es256 | AlgorithmType::Es384 => parse_ecdsa_to_rs(signed_data.as_slice())?, + AlgorithmType::Es256 | AlgorithmType::Es384 => parse_ecdsa_to_rs(signed_data.as_slice())?, _ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm), }; @@ -118,14 +139,21 @@ pub fn merge_header_claims(header: &[u8], claims: &[u8]) -> Vec { tobe_signed } -pub fn merge_payload_claims<'a>(payload: &'a Option, claims: &'a Map) -> XResult> { +pub fn merge_payload_claims<'a>( + payload: &'a Option, + claims: &'a Map, +) -> XResult> { Ok(match (payload, claims.is_empty()) { - (Some(payload), true) => Cow::Owned(util::base64_encode_url_safe_no_pad(payload.as_bytes())), + (Some(payload), true) => { + Cow::Owned(util::base64_encode_url_safe_no_pad(payload.as_bytes())) + } (_, _) => opt_result!(claims.to_base64(), "Claims to base64 failed: {}"), }) } -pub fn build_jwt_parts(sub_arg_matches: &ArgMatches) -> XResult<(Header, Option, Map)> { +pub fn build_jwt_parts( + sub_arg_matches: &ArgMatches, +) -> XResult<(Header, Option, Map)> { let key_id = sub_arg_matches.value_of("key-id"); let claims = sub_arg_matches.values_of("claims"); let payload = sub_arg_matches.value_of("payload"); @@ -145,8 +173,12 @@ pub fn build_jwt_parts(sub_arg_matches: &ArgMatches) -> XResult<(Header, Option< jwt_claims.insert(k, v); }); } - Ok(value) => { warning!("Not valid payload map: {}", value); } - Err(e) => { warning!("Not valid payload value: {}", e); } + Ok(value) => { + warning!("Not valid payload map: {}", value); + } + Err(e) => { + warning!("Not valid payload value: {}", e); + } }; } @@ -155,8 +187,12 @@ pub fn build_jwt_parts(sub_arg_matches: &ArgMatches) -> XResult<(Header, Option< (_, Some(claims)) => { for claim in claims { match split_claim(claim) { - None => { warning!("Claim '{}' do not contains ':'", claim); } - Some((k, v)) => { jwt_claims.insert(k, v); } + None => { + warning!("Claim '{}' do not contains ':'", claim); + } + Some((k, v)) => { + jwt_claims.insert(k, v); + } } } if !jwt_claims.contains_key("sub") { @@ -168,15 +204,23 @@ pub fn build_jwt_parts(sub_arg_matches: &ArgMatches) -> XResult<(Header, Option< // set jti, iat and sub if jti && !jwt_claims.contains_key("jti") { - jwt_claims.insert("jti".to_string(), Value::String(format!("jti-{}", util_time::get_current_millis()))); + jwt_claims.insert( + "jti".to_string(), + Value::String(format!("jti-{}", util_time::get_current_millis())), + ); } if let Some(validity) = validity { match util_time::parse_duration(validity) { - None => { warning!("Bad validity: {}", validity) } + None => { + warning!("Bad validity: {}", validity) + } Some(validity) => { let current_secs = (util_time::get_current_millis() / 1000) as u64; jwt_claims.insert("iat".to_string(), Value::Number(Number::from(current_secs))); - jwt_claims.insert("exp".to_string(), Value::Number(Number::from(current_secs + validity.as_secs()))); + jwt_claims.insert( + "exp".to_string(), + Value::Number(Number::from(current_secs + validity.as_secs())), + ); } } } diff --git a/src/cmd_sign_jwt_se.rs b/src/cmd_sign_jwt_se.rs index 18344ee..54fc280 100644 --- a/src/cmd_sign_jwt_se.rs +++ b/src/cmd_sign_jwt_se.rs @@ -4,14 +4,14 @@ use clap::{App, Arg, ArgMatches, SubCommand}; use jwt::{AlgorithmType, Header, ToBase64}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::{util_msg, XResult}; +use rust_util::XResult; use serde_json::{Map, Value}; use std::collections::BTreeMap; use crate::cmd_sign_jwt::{build_jwt_parts, merge_header_claims, merge_payload_claims}; use crate::ecdsautil::parse_ecdsa_to_rs; use crate::keyutil::{parse_key_uri, KeyUri}; -use crate::{hmacutil, util}; +use crate::{cmd_sign_jwt, cmdutil, hmacutil, util}; const SEPARATOR: &str = "."; @@ -23,21 +23,14 @@ impl Command for CommandImpl { } fn subcommand<'a>(&self) -> App<'a, 'a> { - SubCommand::with_name(self.name()).about("Sign JWT subcommand") + let app = SubCommand::with_name(self.name()).about("Sign JWT subcommand") .arg(Arg::with_name("key").long("key").required(true).takes_value(true).help("Key uri")) - .arg(Arg::with_name("key-id").short("K").long("key-id").takes_value(true).help("Header key ID")) - .arg(Arg::with_name("claims").short("C").long("claims").takes_value(true).multiple(true).help("Claims, key:value")) - .arg(Arg::with_name("payload").short("P").long("payload").takes_value(true).help("Claims in JSON")) - .arg(Arg::with_name("jti").long("jti").help("Claims jti")) - .arg(Arg::with_name("validity").long("validity").takes_value(true).help("Claims validity period e.g. 10m means 10 minutes (s - second, m - minute, h - hour, d - day)")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()); + cmd_sign_jwt::fill_sign_jwt_app_args(app) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } + let json_output = cmdutil::check_json_output(sub_arg_matches); let mut json = BTreeMap::<&'_ str, String>::new(); diff --git a/src/cmd_sign_jwt_soft.rs b/src/cmd_sign_jwt_soft.rs index f6daf2d..912cf48 100644 --- a/src/cmd_sign_jwt_soft.rs +++ b/src/cmd_sign_jwt_soft.rs @@ -3,12 +3,12 @@ use std::collections::BTreeMap; use clap::{App, Arg, ArgMatches, SubCommand}; use jwt::{AlgorithmType, Header, ToBase64}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::{util_msg, XResult}; +use rust_util::XResult; use serde_json::{Map, Value}; use crate::cmd_sign_jwt::{build_jwt_parts, merge_header_claims, merge_payload_claims}; use crate::keychain::{KeychainKey, KeychainKeyValue}; -use crate::{digest, ecdsautil, hmacutil, keychain, rsautil, util}; +use crate::{cmd_sign_jwt, cmdutil, digest, ecdsautil, hmacutil, keychain, rsautil, util}; const SEPARATOR: &str = "."; @@ -20,21 +20,14 @@ impl Command for CommandImpl { } fn subcommand<'a>(&self) -> App<'a, 'a> { - SubCommand::with_name(self.name()).about("Sign JWT subcommand") + let app = SubCommand::with_name(self.name()).about("Sign JWT subcommand") .arg(Arg::with_name("private-key").short("k").long("private-key").takes_value(true).help("Private key PKCS#8")) - .arg(Arg::with_name("key-id").short("K").long("key-id").takes_value(true).help("Header key ID")) - .arg(Arg::with_name("claims").short("C").long("claims").takes_value(true).multiple(true).help("Claims, key:value")) - .arg(Arg::with_name("payload").short("P").long("payload").takes_value(true).help("Claims in JSON")) - .arg(Arg::with_name("jti").long("jti").help("Claims jti")) - .arg(Arg::with_name("validity").long("validity").takes_value(true).help("Claims validity period e.g. 10m means 10 minutes (s - second, m - minute, h - hour, d - day)")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()); + cmd_sign_jwt::fill_sign_jwt_app_args(app) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { - util_msg::set_logger_std_out(false); - } + let json_output = cmdutil::check_json_output(sub_arg_matches); let mut json = BTreeMap::<&'_ str, String>::new(); diff --git a/src/cmd_ssh_piv_cert.rs b/src/cmd_ssh_piv_cert.rs index 35b56fb..19e3d46 100644 --- a/src/cmd_ssh_piv_cert.rs +++ b/src/cmd_ssh_piv_cert.rs @@ -15,7 +15,7 @@ use yubikey::{Key, YubiKey}; use crate::digest::{sha256_bytes, sha384_bytes}; use crate::pivutil::slot_equals; -use crate::{pinutil, pivutil, util}; +use crate::{cmdutil, pinutil, pivutil, util}; pub struct CommandImpl; @@ -27,9 +27,9 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("SSH PIV sign cert subcommand") - .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")) + .arg(cmdutil::build_pin_arg()) .arg(Arg::with_name("no-pin").long("no-pin").help("No PIN")) - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) + .arg(cmdutil::build_slot_arg()) .arg(Arg::with_name("key-id").short("k").long("key-id").takes_value(true).default_value("default_key_id").help("SSH user CA key id")) .arg(Arg::with_name("principal").short("P").long("principal").takes_value(true).default_value("root").multiple(true).help("SSH user CA principal")) .arg(Arg::with_name("pub").short("f").long("pub").alias("pub-file").required(true).takes_value(true).help("SSH public key file")) diff --git a/src/cmd_ssh_piv_sign.rs b/src/cmd_ssh_piv_sign.rs index a9405ea..16f739e 100644 --- a/src/cmd_ssh_piv_sign.rs +++ b/src/cmd_ssh_piv_sign.rs @@ -6,7 +6,7 @@ use rust_util::util_msg; use yubikey::{Key, YubiKey}; use yubikey::piv::{AlgorithmId, sign_data}; -use crate::{pinutil, pivutil, util}; +use crate::{cmdutil, pinutil, pivutil, util}; use crate::pivutil::{get_algorithm_id_by_certificate, slot_equals, ToStr}; use crate::sshutil::SshVecWriter; @@ -18,9 +18,9 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()).about("SSH piv sign subcommand") - .arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")) + .arg(cmdutil::build_slot_arg()) + .arg(cmdutil::build_pin_arg()) .arg(Arg::with_name("no-pin").long("no-pin").help("No PIN")) - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) .arg(Arg::with_name("namespace").short("n").long("namespace").takes_value(true).help("Namespace")) .arg(Arg::with_name("in").long("in").required(true).takes_value(true).help("In file, - for stdin")) } diff --git a/src/cmd_ssh_pub_key.rs b/src/cmd_ssh_pub_key.rs index bd1d6cb..7237b5d 100644 --- a/src/cmd_ssh_pub_key.rs +++ b/src/cmd_ssh_pub_key.rs @@ -1,30 +1,39 @@ use crate::digest::sha256_bytes; -use crate::pivutil; use crate::pivutil::{get_algorithm_id_by_certificate, slot_equals, ToStr}; use crate::sshutil::SshVecWriter; +use crate::{cmdutil, pivutil}; use base64::engine::general_purpose::STANDARD; use base64::Engine; use clap::{App, Arg, ArgMatches, SubCommand}; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; +use std::collections::BTreeMap; use yubikey::piv::AlgorithmId; use yubikey::{Key, YubiKey}; pub struct CommandImpl; impl Command for CommandImpl { - fn name(&self) -> &str { "ssh-pub-key" } + fn name(&self) -> &str { + "ssh-pub-key" + } fn subcommand<'a>(&self) -> App<'a, 'a> { - SubCommand::with_name(self.name()).about("SSH public key subcommand") - .arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")) + SubCommand::with_name(self.name()) + .about("SSH public key subcommand") + .arg(cmdutil::build_slot_arg()) .arg(Arg::with_name("ca").long("ca").help("SSH cert-authority")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - util_msg::set_logger_std_out(false); + let json_output = cmdutil::check_json_output(sub_arg_matches); - let slot = opt_value_result!(sub_arg_matches.value_of("slot"), "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"); + let mut json = BTreeMap::<&'_ str, String>::new(); + + let slot = opt_value_result!( + sub_arg_matches.value_of("slot"), + "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e" + ); let ca = sub_arg_matches.is_present("ca"); let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}"); let slot_id = pivutil::get_slot_id(slot)?; @@ -33,19 +42,28 @@ impl Command for CommandImpl { let mut ec_key_point = vec![]; match Key::list(&mut yk) { Err(e) => warning!("List keys failed: {}", e), - Ok(keys) => for k in &keys { - let slot_str = format!("{:x}", Into::::into(k.slot())); - if slot_equals(&slot_id, &slot_str) { - let cert = &k.certificate().cert.tbs_certificate; - let certificate = k.certificate(); - if let Ok(algorithm_id) = get_algorithm_id_by_certificate(certificate) { - match algorithm_id { - AlgorithmId::EccP256 | AlgorithmId::EccP384 => { - let public_key_bit_string = &cert.subject_public_key_info.subject_public_key; - ec_key_point.extend_from_slice(public_key_bit_string.raw_bytes()); - algorithm_id_opt = Some(algorithm_id); + Ok(keys) => { + for k in &keys { + let slot_str = format!("{:x}", Into::::into(k.slot())); + if slot_equals(&slot_id, &slot_str) { + let cert = &k.certificate().cert.tbs_certificate; + let certificate = k.certificate(); + if let Ok(algorithm_id) = get_algorithm_id_by_certificate(certificate) { + match algorithm_id { + AlgorithmId::EccP256 | AlgorithmId::EccP384 => { + let public_key_bit_string = + &cert.subject_public_key_info.subject_public_key; + ec_key_point + .extend_from_slice(public_key_bit_string.raw_bytes()); + algorithm_id_opt = Some(algorithm_id); + } + _ => { + return simple_error!( + "Not P256/384 key: {}", + algorithm_id.to_str() + ) + } } - _ => return simple_error!("Not P256/384 key: {}", algorithm_id.to_str()), } } } @@ -82,18 +100,34 @@ impl Command for CommandImpl { ssh_pub_key.write_bytes(&ecc_key_blob); let ssh_pub_key_sha256 = sha256_bytes(&ssh_pub_key); - information!("SSH key SHA256: {} (base64)", STANDARD.encode(&ssh_pub_key_sha256)); + information!( + "SSH key SHA256: {} (base64)", + STANDARD.encode(&ssh_pub_key_sha256) + ); information!("SSH key SHA256: {} (hex)", hex::encode(&ssh_pub_key_sha256)); eprintln!(); - println!( + let ssh_pub_key = format!( "{}ecdsa-sha2-{} {} Yubikey-PIV-{}", - if ca { "cert-authority,principals=\"root\" " } else { "" }, + if ca { + "cert-authority,principals=\"root\" " + } else { + "" + }, ssh_algorithm, STANDARD.encode(&ssh_pub_key), slot_id ); + if json_output { + json.insert("ssh_pub_key", ssh_pub_key); + } else { + println!("{}", &ssh_pub_key); + } + + if json_output { + println!("{}", serde_json::to_string_pretty(&json).unwrap()); + } Ok(None) } } diff --git a/src/cmd_u2f_register.rs b/src/cmd_u2f_register.rs index 964210f..825d040 100644 --- a/src/cmd_u2f_register.rs +++ b/src/cmd_u2f_register.rs @@ -9,11 +9,10 @@ use openssl::hash::MessageDigest; use openssl::pkey::PKey; use openssl::sign::Verifier; use rust_util::util_clap::{Command, CommandError}; -use rust_util::util_msg; use x509_parser::certificate::X509Certificate; use x509_parser::prelude::FromDer; -use crate::digest; +use crate::{cmdutil, digest}; use crate::fido; use crate::fido::{U2fRegistrationData, U2fV2Challenge}; use crate::util::base64_encode; @@ -29,12 +28,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("timeout").short("t").long("timeout").default_value("30").help("Timeout in seconds")) .arg(Arg::with_name("challenge").long("challenge").takes_value(true).help("Challenge HEX")) .arg(Arg::with_name("challenge-with-timestamp-prefix").long("challenge-with-timestamp-prefix").help("Challenge with timestamp prefix")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let timeout_ms = match sub_arg_matches.value_of("timeout").unwrap().parse::() { Ok(t) => (t * 1000) as u64, diff --git a/src/cmd_u2f_sign.rs b/src/cmd_u2f_sign.rs index 9de73cc..05a45bf 100644 --- a/src/cmd_u2f_sign.rs +++ b/src/cmd_u2f_sign.rs @@ -13,7 +13,7 @@ use openssl::pkey::PKey; use openssl::sign::Verifier; use rust_util::util_clap::{Command, CommandError}; -use crate::digest; +use crate::{cmdutil, digest}; use crate::fido; use crate::fido::U2fV2Challenge; use crate::util::base64_encode; @@ -31,12 +31,11 @@ impl Command for CommandImpl { .arg(Arg::with_name("challenge").long("challenge").takes_value(true).help("Challenge HEX")) .arg(Arg::with_name("challenge-with-timestamp-prefix").long("challenge-with-timestamp-prefix").help("Challenge with timestamp prefix")) .arg(Arg::with_name("key-handle").short("k").long("key-handle").takes_value(true).multiple(true).help("Key handle")) - .arg(Arg::with_name("json").long("json").help("JSON output")) + .arg(cmdutil::build_json_arg()) } fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { - let json_output = sub_arg_matches.is_present("json"); - if json_output { rust_util::util_msg::set_logger_std_out(false); } + let json_output = cmdutil::check_json_output(sub_arg_matches); let timeout_ms = match sub_arg_matches.value_of("timeout").unwrap().parse::() { Ok(t) => (t * 1000) as u64, diff --git a/src/cmdutil.rs b/src/cmdutil.rs new file mode 100644 index 0000000..2646670 --- /dev/null +++ b/src/cmdutil.rs @@ -0,0 +1,33 @@ +use clap::{Arg, ArgMatches}; +use rust_util::util_msg; + +pub fn build_slot_arg() -> Arg<'static, 'static> { + Arg::with_name("slot") + .short("s") + .long("slot") + .takes_value(true) + .help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e") +} + +pub fn build_pin_arg() -> Arg<'static, 'static> { + Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN") +} + +pub fn build_keychain_name_arg() -> Arg<'static, 'static> { + Arg::with_name("keychain-name") + .long("keychain-name") + .takes_value(true) + .help("Key chain name") +} + +pub fn build_json_arg() -> Arg<'static, 'static> { + Arg::with_name("json").long("json").help("JSON output") +} + +pub fn check_json_output(sub_arg_matches: &ArgMatches) -> bool { + let json_output = sub_arg_matches.is_present("json"); + if json_output { + util_msg::set_logger_std_out(false); + } + json_output +} diff --git a/src/main.rs b/src/main.rs index e6e9a0f..fa1c2e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,6 +71,7 @@ mod signfile; mod sshutil; mod util; mod keychain; +mod cmdutil; pub struct DefaultCommandImpl;