diff --git a/Cargo.lock b/Cargo.lock index 0210e59..891d0eb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,7 +508,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.13.1" +version = "1.13.2" dependencies = [ "aes-gcm-stream", "authenticator 0.3.1", diff --git a/Cargo.toml b/Cargo.toml index f8647e6..b155e4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.13.1" +version = "1.13.2" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_external_public_key.rs b/src/cmd_external_public_key.rs index 2da3dcd..6693c28 100644 --- a/src/cmd_external_public_key.rs +++ b/src/cmd_external_public_key.rs @@ -55,7 +55,7 @@ fn fetch_public_key(parameter: &str, serial_opt: &Option<&str>) -> XResult) -> XResult { if key.algorithm.is_ecc() { - let private_key = cmd_hmac_decrypt::try_decrypt(&key.hmac_enc_private_key)?; + let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &key.hmac_enc_private_key)?; let p256_public_key = ecdsautil::parse_p256_private_key_to_public_key(&private_key).ok(); let p384_public_key = ecdsautil::parse_p384_private_key_to_public_key(&private_key).ok(); let p521_public_key = ecdsautil::parse_p521_private_key_to_public_key(&private_key).ok(); @@ -90,7 +90,7 @@ fn fetch_public_key(parameter: &str, serial_opt: &Option<&str>) -> XResult { @@ -96,7 +96,7 @@ pub fn sign(alg: &str, message: &[u8], key_uri: KeyUri, sub_arg_matches: &ArgMat } KeyUri::YubikeyHmacEncSoftKey(key) => { if key.algorithm.is_ecc() { - let private_key = cmd_hmac_decrypt::try_decrypt(&key.hmac_enc_private_key)?; + let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &key.hmac_enc_private_key)?; let (jwt_algorithm, private_key_d) = parse_ecdsa_private_key(&private_key)?; let raw_in = digest_by_jwt_algorithm(jwt_algorithm, message)?; @@ -111,7 +111,7 @@ pub fn sign(alg: &str, message: &[u8], key_uri: KeyUri, sub_arg_matches: &ArgMat Ok(signed_data) } else if key.algorithm.is_rsa() { use rsa::pkcs8::DecodePrivateKey; - let private_key = cmd_hmac_decrypt::try_decrypt(&key.hmac_enc_private_key)?; + let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &key.hmac_enc_private_key)?; let private_key_der = base64_decode(&private_key)?; let rsa_private_key = RsaPrivateKey::from_pkcs8_der(&private_key_der)?; diff --git a/src/cmd_hmac_decrypt.rs b/src/cmd_hmac_decrypt.rs index 382d700..83edc20 100644 --- a/src/cmd_hmac_decrypt.rs +++ b/src/cmd_hmac_decrypt.rs @@ -15,14 +15,10 @@ impl Command for CommandImpl { fn subcommand<'a>(&self) -> App<'a, 'a> { SubCommand::with_name(self.name()) .about("YubiKey HMAC decrypt") - .arg( - Arg::with_name("ciphertext") - .long("ciphertext") - .takes_value(true) - .required(true) - .help("Ciphertext"), - ) + .arg(Arg::with_name("ciphertext").long("ciphertext").takes_value(true).required(true).help("Ciphertext"), ) .arg(Arg::with_name("auto-pbe").long("auto-pbe").help("Auto PBE decryption")) + .arg(Arg::with_name("password").long("password").takes_value(true).help("Password")) + .arg(Arg::with_name("outputs-password").long("outputs-password").help("Outputs password")) .arg(cmdutil::build_json_arg()) } @@ -30,13 +26,18 @@ impl Command for CommandImpl { let json_output = cmdutil::check_json_output(sub_arg_matches); let ciphertext = sub_arg_matches.value_of("ciphertext").unwrap(); + let mut pin_opt = sub_arg_matches.value_of("password").map(|p| p.to_string()); let auto_pbe = sub_arg_matches.is_present("auto-pbe"); + let outputs_password = sub_arg_matches.is_present("outputs-password"); - let text = try_decrypt_with_pbe_option(ciphertext, auto_pbe)?; + let text = try_decrypt_with_pbe_option(&mut pin_opt, ciphertext, auto_pbe)?; if json_output { let mut json = BTreeMap::<&'_ str, String>::new(); json.insert("plaintext", text); + if let (true, Some(pin)) = (outputs_password, pin_opt.as_ref()) { + json.insert("password", pin.to_string()); + } util::print_pretty_json(&json); } else { success!("Plaintext: {}", text); @@ -45,24 +46,24 @@ impl Command for CommandImpl { } } -pub fn try_decrypt(ciphertext: &str) -> XResult { - try_decrypt_with_pbe_option(ciphertext, true) +pub fn try_decrypt(pin_opt: &mut Option,ciphertext: &str) -> XResult { + try_decrypt_with_pbe_option(pin_opt, ciphertext, true) } -pub fn try_decrypt_with_pbe_option(ciphertext: &str, auto_pbe: bool) -> XResult { +pub fn try_decrypt_with_pbe_option(pin_opt: &mut Option, ciphertext: &str, auto_pbe: bool) -> XResult { if is_hmac_encrypted(ciphertext) { - hmac_decrypt(ciphertext, auto_pbe) + hmac_decrypt(pin_opt, ciphertext, auto_pbe) } else if pbeutil::is_simple_pbe_encrypted(ciphertext) { - pbeutil::simple_pbe_decrypt_with_prompt_to_string(&ciphertext) + pbeutil::simple_pbe_decrypt_with_prompt_to_string(pin_opt,&ciphertext) } else { Ok(ciphertext.to_string()) } } -pub fn hmac_decrypt(ciphertext: &str, auto_pbe: bool) -> XResult { +pub fn hmac_decrypt(pin_opt: &mut Option, ciphertext: &str, auto_pbe: bool) -> XResult { let text = hmac_decrypt_to_string(ciphertext)?; if auto_pbe && pbeutil::is_simple_pbe_encrypted(&text) { - pbeutil::simple_pbe_decrypt_with_prompt_to_string(&text) + pbeutil::simple_pbe_decrypt_with_prompt_to_string(pin_opt, &text) } else { Ok(text) } diff --git a/src/cmd_se_ecdh.rs b/src/cmd_se_ecdh.rs index dfca8d1..e4725c1 100644 --- a/src/cmd_se_ecdh.rs +++ b/src/cmd_se_ecdh.rs @@ -58,7 +58,7 @@ impl Command for CommandImpl { opt_result!(hex::decode(epk), "Decode public key from hex failed: {}") }; - let private_key = cmd_hmac_decrypt::try_decrypt(&se_key_uri.private_key)?; + let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &se_key_uri.private_key)?; let dh = seutil::secure_enclave_p256_dh( &private_key, &ephemeral_public_key_der_bytes, diff --git a/src/cmd_se_ecsign.rs b/src/cmd_se_ecsign.rs index d3630ad..ef0c0ea 100644 --- a/src/cmd_se_ecsign.rs +++ b/src/cmd_se_ecsign.rs @@ -49,7 +49,7 @@ impl Command for CommandImpl { let se_key_uri = key_uri.as_secure_enclave_key()?; debugging!("Secure enclave key URI: {:?}", se_key_uri); - let private_key = cmd_hmac_decrypt::try_decrypt(&se_key_uri.private_key)?; + let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &se_key_uri.private_key)?; let signature = seutil::secure_enclave_p256_sign(&private_key, &input_bytes)?; if json_output { diff --git a/src/cmd_se_recover.rs b/src/cmd_se_recover.rs index 415f5e9..3a0fd96 100644 --- a/src/cmd_se_recover.rs +++ b/src/cmd_se_recover.rs @@ -27,7 +27,7 @@ impl Command for CommandImpl { let se_key_uri = key_uri.as_secure_enclave_key()?; debugging!("Secure enclave key URI: {:?}", se_key_uri); - let private_key = cmd_hmac_decrypt::try_decrypt(&se_key_uri.private_key)?; + let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &se_key_uri.private_key)?; let (public_key_point, public_key_der, _private_key) = seutil::recover_secure_enclave_p256_public_key( &private_key, diff --git a/src/cmd_sign_jwt_se.rs b/src/cmd_sign_jwt_se.rs index 8038b88..854c1d2 100644 --- a/src/cmd_sign_jwt_se.rs +++ b/src/cmd_sign_jwt_se.rs @@ -34,7 +34,7 @@ impl Command for CommandImpl { sub_arg_matches.value_of("key"), "Private key PKCS#8 DER base64 encoded or PEM" ); - let private_key = cmd_hmac_decrypt::try_decrypt(private_key)?; + let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, private_key)?; let key_uri = parse_key_uri(&private_key)?; let se_key_uri = key_uri.as_secure_enclave_key()?; debugging!("Secure enclave key URI: {:?}", se_key_uri); diff --git a/src/cmd_sign_jwt_soft.rs b/src/cmd_sign_jwt_soft.rs index c45b625..9f66e5a 100644 --- a/src/cmd_sign_jwt_soft.rs +++ b/src/cmd_sign_jwt_soft.rs @@ -33,7 +33,7 @@ impl Command for CommandImpl { "Private key PKCS#8 DER base64 encoded or PEM" ); - let private_key = cmd_hmac_decrypt::try_decrypt(private_key)?; + let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, private_key)?; let private_key = if keychain::is_keychain_key_uri(&private_key) { debugging!("Private key keychain key URI: {}", &private_key); diff --git a/src/pbeutil.rs b/src/pbeutil.rs index 30bb991..6f37a45 100644 --- a/src/pbeutil.rs +++ b/src/pbeutil.rs @@ -11,8 +11,8 @@ pub fn simple_pbe_encrypt_with_prompt_from_string(iteration: u32, plaintext: &st simple_pbe_encrypt_with_prompt(iteration, plaintext.as_bytes(), passowrd, password_double_check) } -pub fn simple_pbe_decrypt_with_prompt_to_string(ciphertext: &str) -> XResult { - let plaintext = simple_pbe_decrypt_with_prompt(ciphertext)?; +pub fn simple_pbe_decrypt_with_prompt_to_string(pin_opt: &mut Option, ciphertext: &str) -> XResult { + let plaintext = simple_pbe_decrypt_with_prompt(pin_opt, ciphertext)?; Ok(String::from_utf8(plaintext)?) } @@ -34,8 +34,9 @@ pub fn simple_pbe_encrypt_with_prompt(iteration: u32, plaintext: &[u8], password simple_pbe_encrypt(&pin, iteration, plaintext) } -pub fn simple_pbe_decrypt_with_prompt(ciphertext: &str) -> XResult> { - let pin = opt_value_result!(pinutil::get_pin(None), "Simple PBE password required"); +pub fn simple_pbe_decrypt_with_prompt(pin_opt: &mut Option, ciphertext: &str) -> XResult> { + let pin = opt_value_result!(pinutil::get_pin(pin_opt.clone().as_deref()), "Simple PBE password required"); + *pin_opt = Some(pin.clone()); simple_pbe_decrypt(&pin, ciphertext) }