From 3dae02e09096ac0817657f35f2a409c8b6d297f2 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Thu, 1 May 2025 23:38:36 +0800 Subject: [PATCH] feat: v1.12.3 --- Cargo.lock | 29 ++++++++++++++++++-- Cargo.toml | 3 ++- src/cmd_ec_verify.rs | 2 +- src/cmd_external_sign.rs | 13 +++++---- src/cmd_piv_verify.rs | 2 +- src/cmd_se.rs | 4 +++ src/cmd_sign_jwt_soft.rs | 17 +++++++----- src/ecdsautil.rs | 58 +++++++++++++++++++--------------------- 8 files changed, 79 insertions(+), 49 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3f5ded8..ff76bfd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -508,7 +508,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.12.2" +version = "1.12.3" dependencies = [ "aes-gcm-stream", "authenticator 0.3.1", @@ -551,6 +551,7 @@ dependencies = [ "swift-secure-enclave-tool-rs", "tabled", "u2f-hatter-fork", + "which 7.0.3", "x509", "x509-parser 0.15.1", "yubico_manager", @@ -1096,6 +1097,12 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + [[package]] name = "env_logger" version = "0.10.2" @@ -2663,7 +2670,7 @@ dependencies = [ "nom 7.1.3", "percent-encoding", "secrecy", - "which", + "which 4.4.2", "zeroize", ] @@ -4518,6 +4525,18 @@ dependencies = [ "rustix 0.38.44", ] +[[package]] +name = "which" +version = "7.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" +dependencies = [ + "either", + "env_home", + "rustix 1.0.5", + "winsafe", +] + [[package]] name = "winapi" version = "0.2.8" @@ -4778,6 +4797,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "wit-bindgen-rt" version = "0.39.0" diff --git a/Cargo.toml b/Cargo.toml index 5aa08aa..f03299a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.12.2" +version = "1.12.3" authors = ["Hatter Jiang "] edition = "2018" @@ -58,6 +58,7 @@ swift-secure-enclave-tool-rs = "0.1" u2f-hatter-fork = "0.2" security-framework = { version = "3.0", features = ["OSX_10_15"] } rsa = "0.9.8" +which = "7.0.3" #lazy_static = "1.4.0" #ssh-key = "0.4.0" #ctap-hid-fido2 = "2.1.3" diff --git a/src/cmd_ec_verify.rs b/src/cmd_ec_verify.rs index 546690d..d50eedb 100644 --- a/src/cmd_ec_verify.rs +++ b/src/cmd_ec_verify.rs @@ -53,7 +53,7 @@ impl Command for CommandImpl { json.insert("signature_hex", hex::encode(&signature)); } - match ecdsautil::ecdsaverify(ecdsa_algorithm, &public_key, &hash_bytes, &signature) { + match ecdsautil::ecdsa_verify(ecdsa_algorithm, &public_key, &hash_bytes, &signature) { Ok(_) => { success!("Verify ECDSA succeed."); if json_output { diff --git a/src/cmd_external_sign.rs b/src/cmd_external_sign.rs index 9a5b7fd..7c3616b 100644 --- a/src/cmd_external_sign.rs +++ b/src/cmd_external_sign.rs @@ -10,7 +10,8 @@ use rust_util::XResult; use serde_json::Value; use std::collections::BTreeMap; use yubikey::piv::{sign_data, AlgorithmId}; -use crate::cmd_sign_jwt_soft::parse_ecdsa_private_key; +use crate::cmd_sign_jwt_soft::{convert_jwt_algorithm_to_ecdsa_algorithm, parse_ecdsa_private_key}; +use crate::ecdsautil::EcdsaSignType; pub struct CommandImpl; @@ -68,7 +69,7 @@ fn sign(sub_arg_matches: &ArgMatches) -> XResult> { let mut yk = yubikeyutil::open_yubikey_with_args(sub_arg_matches)?; let pin_opt = pivutil::check_read_pin(&mut yk, key.slot, sub_arg_matches); - // FIXME Check Yubikey slot algorithm + // FIXME Check YubiKey slot algorithm let jwt_algorithm = get_jwt_algorithm(&key, alg)?; if let Some(pin) = pin_opt { @@ -90,11 +91,9 @@ fn sign(sub_arg_matches: &ArgMatches) -> XResult> { let (jwt_algorithm, private_key_d) = parse_ecdsa_private_key(&private_key)?; let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?; - let signed_data = match jwt_algorithm { - AlgorithmType::Es256 => ecdsautil::sign_p256_der(&private_key_d, &raw_in)?, - AlgorithmType::Es384 => ecdsautil::sign_p384_der(&private_key_d, &raw_in)?, - _ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm), - }; + let ecdsa_algorithm = convert_jwt_algorithm_to_ecdsa_algorithm(jwt_algorithm)?; + let signed_data = ecdsautil::ecdsa_sign(ecdsa_algorithm, &private_key_d, &raw_in, EcdsaSignType::Der)?; + Ok(signed_data) } } diff --git a/src/cmd_piv_verify.rs b/src/cmd_piv_verify.rs index 29f8b95..05ab93b 100644 --- a/src/cmd_piv_verify.rs +++ b/src/cmd_piv_verify.rs @@ -58,7 +58,7 @@ impl Command for CommandImpl { } let algorithm = iff!(algorithm_id == AlgorithmId::EccP256, EcdsaAlgorithm::P256, EcdsaAlgorithm::P384); - match ecdsautil::ecdsaverify(algorithm, pk_point, &hash_bytes, &signature) { + match ecdsautil::ecdsa_verify(algorithm, pk_point, &hash_bytes, &signature) { Ok(_) => { success!("Verify ECDSA succeed."); if json_output { diff --git a/src/cmd_se.rs b/src/cmd_se.rs index 33c0903..e24e485 100644 --- a/src/cmd_se.rs +++ b/src/cmd_se.rs @@ -19,6 +19,10 @@ impl Command for CommandImpl { fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError { let json_output = cmdutil::check_json_output(sub_arg_matches); + if let Err(_) = which::which("swift-secure-enclave-tool") { + failure!("Secure Enclave tool not found."); + } + if json_output { let mut json = BTreeMap::new(); json.insert("se_supported", seutil::is_support_se()); diff --git a/src/cmd_sign_jwt_soft.rs b/src/cmd_sign_jwt_soft.rs index cea241f..1482435 100644 --- a/src/cmd_sign_jwt_soft.rs +++ b/src/cmd_sign_jwt_soft.rs @@ -7,6 +7,7 @@ use serde_json::{Map, Value}; use crate::cmd_sign_jwt::{build_jwt_parts, digest_by_jwt_algorithm, merge_header_claims, merge_payload_claims, print_jwt_token}; use crate::keychain::{KeychainKey, KeychainKeyValue}; use crate::{cmd_sign_jwt, cmdutil, ecdsautil, hmacutil, keychain, util}; +use crate::ecdsautil::{EcdsaAlgorithm, EcdsaSignType}; const SEPARATOR: &str = "."; @@ -76,18 +77,22 @@ fn sign_jwt( let tobe_signed = merge_header_claims(header.as_bytes(), claims.as_bytes()); let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &tobe_signed)?; - - let signed_data = match jwt_algorithm { - AlgorithmType::Es256 => ecdsautil::sign_p256_rs(&private_key_d, &raw_in)?, - AlgorithmType::Es384 => ecdsautil::sign_p384_rs(&private_key_d, &raw_in)?, - _ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm), - }; + let ecdsa_algorithm = convert_jwt_algorithm_to_ecdsa_algorithm(jwt_algorithm)?; + let signed_data = ecdsautil::ecdsa_sign(ecdsa_algorithm, &private_key_d, &raw_in, EcdsaSignType::Rs)?; let signature = util::base64_encode_url_safe_no_pad(&signed_data); Ok([&*header, &*claims, &signature].join(SEPARATOR)) } +pub fn convert_jwt_algorithm_to_ecdsa_algorithm(jwt_algorithm: AlgorithmType) -> XResult { + match jwt_algorithm { + AlgorithmType::Es256 => Ok(EcdsaAlgorithm::P256), + AlgorithmType::Es384 => Ok(EcdsaAlgorithm::P384), + _ => simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm), + } +} + pub fn parse_ecdsa_private_key(private_key: &str) -> XResult<(AlgorithmType, Vec)> { let p256_private_key_d = ecdsautil::parse_p256_private_key(private_key).ok(); let p384_private_key_d = ecdsautil::parse_p384_private_key(private_key).ok(); diff --git a/src/ecdsautil.rs b/src/ecdsautil.rs index b77c7df..fe24432 100644 --- a/src/ecdsautil.rs +++ b/src/ecdsautil.rs @@ -16,6 +16,11 @@ pub enum EcdsaAlgorithm { P384, } +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum EcdsaSignType { + Der, Rs, +} + pub fn parse_ecdsa_to_rs(signature_der: &[u8]) -> XResult> { let (mut r, s)= parse_ecdsa_r_and_s(signature_der)?; r.extend_from_slice(&s); @@ -132,44 +137,35 @@ pub fn parse_p384_private_key(private_key_pkcs8: &str) -> XResult> { parse_ecdsa_private_key!(p384, private_key_pkcs8) } -pub fn sign_p256_rs(private_key_d: &[u8], pre_hash: &[u8]) -> XResult> { - use p256::ecdsa::{SigningKey, Signature}; - use p256::ecdsa::signature::hazmat::PrehashSigner; +macro_rules! sign_ecdsa_rs_or_der { + ($algo: tt, $private_key_d: tt, $pre_hash: tt, $is_rs: tt) => ({ + use $algo::ecdsa::{SigningKey, Signature}; + use $algo::ecdsa::signature::hazmat::PrehashSigner; - let signing_key = SigningKey::from_slice(private_key_d)?; - let signature: Signature = signing_key.sign_prehash(pre_hash)?; + let signing_key = SigningKey::from_slice($private_key_d)?; + let signature: Signature = signing_key.sign_prehash($pre_hash)?; - Ok(signature.to_bytes().to_vec()) + if $is_rs { + Ok(signature.to_bytes().to_vec()) + } else { + Ok(signature.to_der().as_bytes().to_vec()) + } + }) } -pub fn sign_p256_der(private_key_d: &[u8], pre_hash: &[u8]) -> XResult> { - use p256::ecdsa::{SigningKey, Signature}; - use p256::ecdsa::signature::hazmat::PrehashSigner; - - let signing_key = SigningKey::from_slice(private_key_d)?; - let signature: Signature = signing_key.sign_prehash(pre_hash)?; - - Ok(signature.to_der().as_bytes().to_vec()) +pub fn ecdsa_sign(algo: EcdsaAlgorithm, private_key_d: &[u8], pre_hash: &[u8], sign_type: EcdsaSignType) -> XResult> { + match algo { + EcdsaAlgorithm::P256 => sign_p256_rs_or_der(private_key_d, pre_hash, sign_type == EcdsaSignType::Rs), + EcdsaAlgorithm::P384 => sign_p384_rs_or_der(private_key_d, pre_hash, sign_type == EcdsaSignType::Rs), + } } -pub fn sign_p384_rs(private_key_d: &[u8], pre_hash: &[u8]) -> XResult> { - use p384::ecdsa::{SigningKey, Signature}; - use p384::ecdsa::signature::hazmat::PrehashSigner; - - let signing_key = SigningKey::from_slice(private_key_d)?; - let signature: Signature = signing_key.sign_prehash(pre_hash)?; - - Ok(signature.to_bytes().to_vec()) +pub fn sign_p256_rs_or_der(private_key_d: &[u8], pre_hash: &[u8], is_rs: bool) -> XResult> { + sign_ecdsa_rs_or_der!(p256, private_key_d, pre_hash, is_rs) } -pub fn sign_p384_der(private_key_d: &[u8], pre_hash: &[u8]) -> XResult> { - use p384::ecdsa::{SigningKey, Signature}; - use p384::ecdsa::signature::hazmat::PrehashSigner; - - let signing_key = SigningKey::from_slice(private_key_d)?; - let signature: Signature = signing_key.sign_prehash(pre_hash)?; - - Ok(signature.to_der().as_bytes().to_vec()) +pub fn sign_p384_rs_or_der(private_key_d: &[u8], pre_hash: &[u8], is_rs: bool) -> XResult> { + sign_ecdsa_rs_or_der!(p384, private_key_d, pre_hash, is_rs) } macro_rules! ecdsa_verify_signature { @@ -187,7 +183,7 @@ macro_rules! ecdsa_verify_signature { }) } -pub fn ecdsaverify(algo: EcdsaAlgorithm, pk_point: &[u8], prehash: &[u8], signature: &[u8]) -> XResult<()> { +pub fn ecdsa_verify(algo: EcdsaAlgorithm, pk_point: &[u8], prehash: &[u8], signature: &[u8]) -> XResult<()> { match algo { EcdsaAlgorithm::P256 => ecdsa_verify_signature!(NistP256, pk_point, prehash, signature), EcdsaAlgorithm::P384 => ecdsa_verify_signature!(NistP384, pk_point, prehash, signature),