feat: v1.13.2

This commit is contained in:
2025-05-09 23:33:11 +08:00
parent 8e4cf5cec8
commit fdf02bc976
11 changed files with 34 additions and 32 deletions

2
Cargo.lock generated
View File

@@ -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",

View File

@@ -1,6 +1,6 @@
[package]
name = "card-cli"
version = "1.13.1"
version = "1.13.2"
authors = ["Hatter Jiang <jht5945@gmail.com>"]
edition = "2018"

View File

@@ -55,7 +55,7 @@ fn fetch_public_key(parameter: &str, serial_opt: &Option<&str>) -> XResult<Vec<u
if key.usage != KeyUsage::Singing {
simple_error!("Not singing key")
} else {
let private_key = cmd_hmac_decrypt::try_decrypt(&key.private_key)?;
let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &key.private_key)?;
let (_, public_key_der, _) =
seutil::recover_secure_enclave_p256_public_key(&private_key, true)?;
Ok(public_key_der)
@@ -73,7 +73,7 @@ fn fetch_public_key(parameter: &str, serial_opt: &Option<&str>) -> XResult<Vec<u
}
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 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<Vec<u
simple_error!("Invalid hmac enc private key")
} 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)?;
Ok(rsa_private_key.to_public_key().to_public_key_der()?.to_vec())

View File

@@ -65,7 +65,7 @@ pub fn sign(alg: &str, message: &[u8], key_uri: KeyUri, sub_arg_matches: &ArgMat
if key.usage != KeyUsage::Singing {
return simple_error!("Not singing key");
}
let private_key = cmd_hmac_decrypt::try_decrypt(&key.private_key)?;
let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &key.private_key)?;
seutil::secure_enclave_p256_sign(&private_key, message)
}
KeyUri::YubikeyPivKey(key) => {
@@ -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)?;

View File

@@ -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<String> {
try_decrypt_with_pbe_option(ciphertext, true)
pub fn try_decrypt(pin_opt: &mut Option<String>,ciphertext: &str) -> XResult<String> {
try_decrypt_with_pbe_option(pin_opt, ciphertext, true)
}
pub fn try_decrypt_with_pbe_option(ciphertext: &str, auto_pbe: bool) -> XResult<String> {
pub fn try_decrypt_with_pbe_option(pin_opt: &mut Option<String>, ciphertext: &str, auto_pbe: bool) -> XResult<String> {
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<String> {
pub fn hmac_decrypt(pin_opt: &mut Option<String>, ciphertext: &str, auto_pbe: bool) -> XResult<String> {
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)
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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);

View File

@@ -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);

View File

@@ -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<String> {
let plaintext = simple_pbe_decrypt_with_prompt(ciphertext)?;
pub fn simple_pbe_decrypt_with_prompt_to_string(pin_opt: &mut Option<String>, ciphertext: &str) -> XResult<String> {
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<Vec<u8>> {
let pin = opt_value_result!(pinutil::get_pin(None), "Simple PBE password required");
pub fn simple_pbe_decrypt_with_prompt(pin_opt: &mut Option<String>, ciphertext: &str) -> XResult<Vec<u8>> {
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)
}