diff --git a/Cargo.lock b/Cargo.lock index a3154e6..e485e72 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -519,7 +519,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.13.14" +version = "1.13.15" dependencies = [ "aes-gcm-stream", "authenticator 0.3.1", diff --git a/Cargo.toml b/Cargo.toml index 6772f37..1e438f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.13.14" +version = "1.13.15" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_external_ecdh.rs b/src/cmd_external_ecdh.rs index c011de4..2eff207 100644 --- a/src/cmd_external_ecdh.rs +++ b/src/cmd_external_ecdh.rs @@ -58,14 +58,14 @@ pub fn ecdh( sub_arg_matches: &ArgMatches, ) -> XResult> { match key_uri { - KeyUri::SecureEnclaveKey(key) => { + KeyUri::SecureEnclave(key) => { if key.usage != KeyUsage::Singing { return simple_error!("Not singing key"); } let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &key.private_key)?; seutil::secure_enclave_p256_dh(&private_key, ephemeral_public_key_bytes) } - KeyUri::YubikeyPivKey(key) => { + KeyUri::YubikeyPiv(key) => { 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); @@ -108,7 +108,7 @@ pub fn ecdh( Ok(decrypted_shared_secret.to_vec()) } - KeyUri::YubikeyHmacEncSoftKey(key) => { + KeyUri::YubikeyHmacEncSoft(key) => { if key.algorithm.is_ecc() { let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &key.hmac_enc_private_key)?; let private_key_bytes = try_decode(&private_key)?; @@ -128,7 +128,7 @@ pub fn ecdh( simple_error!("Invalid algorithm: {}", key.algorithm.to_str()) } } - KeyUri::ExternalCommandKey(key) => { + KeyUri::ExternalCommand(key) => { let parameter = cmd_hmac_decrypt::try_decrypt(&mut None, &key.parameter)?; external_command_rs::external_ecdh(&key.external_command, ¶meter, ephemeral_public_key_bytes) } diff --git a/src/cmd_external_public_key.rs b/src/cmd_external_public_key.rs index 34bcbb1..69a2b54 100644 --- a/src/cmd_external_public_key.rs +++ b/src/cmd_external_public_key.rs @@ -51,7 +51,7 @@ impl Command for CommandImpl { fn fetch_public_key(parameter: &str, serial_opt: &Option<&str>) -> XResult> { let key_uri = parse_key_uri(parameter)?; match key_uri { - KeyUri::SecureEnclaveKey(key) => { + KeyUri::SecureEnclave(key) => { if key.usage != KeyUsage::Singing { simple_error!("Not singing key") } else { @@ -61,7 +61,7 @@ fn fetch_public_key(parameter: &str, serial_opt: &Option<&str>) -> XResult { + KeyUri::YubikeyPiv(key) => { let mut yk = yubikeyutil::open_yubikey_with_serial(serial_opt)?; if let Some(key) = find_key_or_error(&mut yk, &key.slot)? { let cert_der = key.certificate().cert.to_der()?; @@ -71,7 +71,7 @@ fn fetch_public_key(parameter: &str, serial_opt: &Option<&str>) -> XResult { + KeyUri::YubikeyHmacEncSoft(key) => { if key.algorithm.is_ecc() { 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(); @@ -98,7 +98,7 @@ fn fetch_public_key(parameter: &str, serial_opt: &Option<&str>) -> XResult { + KeyUri::ExternalCommand(key) => { let parameter = cmd_hmac_decrypt::try_decrypt(&mut None, &key.parameter)?; external_command_rs::external_public_key(&key.external_command, ¶meter) } diff --git a/src/cmd_external_sign.rs b/src/cmd_external_sign.rs index 61ded06..bd2b46e 100644 --- a/src/cmd_external_sign.rs +++ b/src/cmd_external_sign.rs @@ -58,7 +58,7 @@ impl Command for CommandImpl { pub fn sign(alg: &str, message: &[u8], key_uri: KeyUri, sub_arg_matches: &ArgMatches) -> XResult> { match key_uri { - KeyUri::SecureEnclaveKey(key) => { + KeyUri::SecureEnclave(key) => { if "ES256" != alg { return simple_error!("Invalid alg: {}", alg); } @@ -68,7 +68,7 @@ pub fn sign(alg: &str, message: &[u8], key_uri: KeyUri, sub_arg_matches: &ArgMat let private_key = cmd_hmac_decrypt::try_decrypt(&mut None, &key.private_key)?; seutil::secure_enclave_p256_sign(&private_key, message) } - KeyUri::YubikeyPivKey(key) => { + KeyUri::YubikeyPiv(key) => { 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); @@ -94,7 +94,7 @@ pub fn sign(alg: &str, message: &[u8], key_uri: KeyUri, sub_arg_matches: &ArgMat ); Ok(signed_data.to_vec()) } - KeyUri::YubikeyHmacEncSoftKey(key) => { + KeyUri::YubikeyHmacEncSoft(key) => { if key.algorithm.is_ecc() { 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)?; @@ -122,9 +122,9 @@ pub fn sign(alg: &str, message: &[u8], key_uri: KeyUri, sub_arg_matches: &ArgMat simple_error!("Invalid algorithm: {}", key.algorithm.to_str()) } } - KeyUri::ExternalCommandKey(key) => { + KeyUri::ExternalCommand(key) => { let parameter = cmd_hmac_decrypt::try_decrypt(&mut None, &key.parameter)?; - let alg = key.algorithm.to_jwa_name(); + let alg = key.algorithm.as_jwa_name(); let signature = external_command_rs::external_sign(&key.external_command, ¶meter, alg, message)?; Ok(signature) } diff --git a/src/cmd_hmac_decrypt.rs b/src/cmd_hmac_decrypt.rs index 1caf379..380103a 100644 --- a/src/cmd_hmac_decrypt.rs +++ b/src/cmd_hmac_decrypt.rs @@ -54,7 +54,7 @@ pub fn try_decrypt_with_pbe_option(pin_opt: &mut Option, ciphertext: &st if is_hmac_encrypted(ciphertext) { hmac_decrypt(pin_opt, ciphertext, auto_pbe) } else if pbeutil::is_simple_pbe_encrypted(ciphertext) { - pbeutil::simple_pbe_decrypt_with_prompt_to_string(pin_opt,&ciphertext) + pbeutil::simple_pbe_decrypt_with_prompt_to_string(pin_opt, ciphertext) } else { Ok(ciphertext.to_string()) } diff --git a/src/cmd_hmac_encrypt.rs b/src/cmd_hmac_encrypt.rs index e5b2b18..9d35786 100644 --- a/src/cmd_hmac_encrypt.rs +++ b/src/cmd_hmac_encrypt.rs @@ -69,7 +69,7 @@ pub fn do_encrypt(text: &str, password_opt: &mut Option, sub_arg_matches let double_pin_check = sub_arg_matches.is_present("double-pin-check"); let iteration = sub_arg_matches.value_of("pbe-iteration") .map(|x| x.parse::().unwrap()).unwrap_or(100000); - pbeutil::simple_pbe_encrypt_with_prompt_from_string(iteration, &text, password_opt, double_pin_check)? + pbeutil::simple_pbe_encrypt_with_prompt_from_string(iteration, text, password_opt, double_pin_check)? } else { text.to_string() }; diff --git a/src/cmd_keypair_generate.rs b/src/cmd_keypair_generate.rs index e60d3bf..d12d93d 100644 --- a/src/cmd_keypair_generate.rs +++ b/src/cmd_keypair_generate.rs @@ -108,7 +108,7 @@ impl Command for CommandImpl { algorithm: algorithm_id, hmac_enc_private_key: pkcs8_base64.clone(), }; - Some(KeyUri::YubikeyHmacEncSoftKey(yubikey_hmac_enc_soft_key).to_string()) + Some(KeyUri::YubikeyHmacEncSoft(yubikey_hmac_enc_soft_key).to_string()) } else { None }; diff --git a/src/cmd_piv_meta.rs b/src/cmd_piv_meta.rs index 65eb89a..75baa84 100644 --- a/src/cmd_piv_meta.rs +++ b/src/cmd_piv_meta.rs @@ -109,7 +109,7 @@ impl Command for CommandImpl { algorithm: KeyAlgorithmId::from_algorithm_id(algorithm_id), slot: slot_id, }; - json.insert("key_uri", KeyUri::YubikeyPivKey(yubikey_piv_key).to_string()); + json.insert("key_uri", KeyUri::YubikeyPiv(yubikey_piv_key).to_string()); } let serial_lower = cert.serial_number.to_string().to_lowercase(); json.insert("serial", if serial_lower.starts_with("00:") { serial_lower.chars().skip(3).collect() } else { serial_lower }); diff --git a/src/cmd_se.rs b/src/cmd_se.rs index e24e485..d6c40af 100644 --- a/src/cmd_se.rs +++ b/src/cmd_se.rs @@ -19,7 +19,7 @@ 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") { + if which::which("swift-secure-enclave-tool").is_err() { failure!("Secure Enclave tool not found."); } diff --git a/src/cmd_se_ecdh.rs b/src/cmd_se_ecdh.rs index 066c2b8..e5ea2af 100644 --- a/src/cmd_se_ecdh.rs +++ b/src/cmd_se_ecdh.rs @@ -31,7 +31,7 @@ impl Command for CommandImpl { let key = sub_arg_matches.value_of("key").unwrap(); let epk = sub_arg_matches.value_of("epk").unwrap(); - let key_uri = parse_key_uri(&key)?; + let key_uri = parse_key_uri(key)?; let se_key_uri = key_uri.as_secure_enclave_key()?; debugging!("Secure enclave key URI: {:?}", se_key_uri); @@ -76,7 +76,7 @@ pub fn parse_epk(epk: &str) -> XResult> { } else { match hex::decode(epk) { Ok(epk_bytes) => Ok(epk_bytes), - Err(e) => match base64_decode(&epk) { + Err(e) => match base64_decode(epk) { Ok(epk_bytes) => Ok(epk_bytes), Err(_) => simple_error!("Decode public key from hex failed: {}", e) } diff --git a/src/cmd_se_ecsign.rs b/src/cmd_se_ecsign.rs index ef0c0ea..0a0db00 100644 --- a/src/cmd_se_ecsign.rs +++ b/src/cmd_se_ecsign.rs @@ -45,7 +45,7 @@ impl Command for CommandImpl { Some(input) => input.as_bytes().to_vec(), }; - let key_uri = parse_key_uri(&key)?; + let key_uri = parse_key_uri(key)?; let se_key_uri = key_uri.as_secure_enclave_key()?; debugging!("Secure enclave key URI: {:?}", se_key_uri); diff --git a/src/cmd_se_recover.rs b/src/cmd_se_recover.rs index 3a0fd96..69e4956 100644 --- a/src/cmd_se_recover.rs +++ b/src/cmd_se_recover.rs @@ -23,7 +23,7 @@ impl Command for CommandImpl { seutil::check_se_supported()?; let key = sub_arg_matches.value_of("key").unwrap(); - let key_uri = parse_key_uri(&key)?; + let key_uri = parse_key_uri(key)?; let se_key_uri = key_uri.as_secure_enclave_key()?; debugging!("Secure enclave key URI: {:?}", se_key_uri); @@ -34,7 +34,7 @@ impl Command for CommandImpl { se_key_uri.usage == KeyUsage::Singing, )?; - print_se_key(json_output, &public_key_point, &public_key_der, &key); + print_se_key(json_output, &public_key_point, &public_key_der, key); Ok(None) } diff --git a/src/keyutil.rs b/src/keyutil.rs index c8e1f4b..2df542d 100644 --- a/src/keyutil.rs +++ b/src/keyutil.rs @@ -8,26 +8,26 @@ use yubikey::piv::{AlgorithmId, SlotId}; // reference: https://git.hatter.ink/hatter/card-cli/issues/6 #[derive(Debug)] pub enum KeyUri { - SecureEnclaveKey(SecureEnclaveKey), - YubikeyPivKey(YubikeyPivKey), - YubikeyHmacEncSoftKey(YubikeyHmacEncSoftKey), - ExternalCommandKey(ExternalCommandKey), + SecureEnclave(SecureEnclaveKey), + YubikeyPiv(YubikeyPivKey), + YubikeyHmacEncSoft(YubikeyHmacEncSoftKey), + ExternalCommand(ExternalCommandKey), } impl KeyUri { pub fn as_secure_enclave_key(&self) -> XResult<&SecureEnclaveKey> { match self { - KeyUri::SecureEnclaveKey(key) => Ok(key), + KeyUri::SecureEnclave(key) => Ok(key), _ => simple_error!("Not a secure enclave key."), } } pub fn get_preferred_algorithm_type(&self) -> AlgorithmType { let algorithm_id = match &self { - KeyUri::SecureEnclaveKey(_) => return AlgorithmType::Es256, - KeyUri::YubikeyPivKey(key) => key.algorithm, - KeyUri::YubikeyHmacEncSoftKey(key) => key.algorithm, - KeyUri::ExternalCommandKey(key) => key.algorithm, + KeyUri::SecureEnclave(_) => return AlgorithmType::Es256, + KeyUri::YubikeyPiv(key) => key.algorithm, + KeyUri::YubikeyHmacEncSoft(key) => key.algorithm, + KeyUri::ExternalCommand(key) => key.algorithm, }; match algorithm_id { KeyAlgorithmId::Rsa1024 @@ -41,6 +41,7 @@ impl KeyUri { } } +#[allow(clippy::to_string_trait_impl)] impl ToString for KeyUri { fn to_string(&self) -> String { let mut key_uri = String::with_capacity(64); @@ -48,15 +49,15 @@ impl ToString for KeyUri { match self { // key://hatter-mac-pro:se/p256:signing:BASE64(dataRepresentation) // key://hatter-mac-pro:se/p256:key_agreement:BASE64(dataRepresentation) - KeyUri::SecureEnclaveKey(key) => { + KeyUri::SecureEnclave(key) => { key_uri.push_str(&key.host); key_uri.push_str(":se/p256:"); key_uri.push_str(&key.usage.to_string()); - key_uri.push_str(":"); + key_uri.push(':'); key_uri.push_str(&key.private_key); } // key://yubikey-5n:piv/p256::9a - KeyUri::YubikeyPivKey(key) => { + KeyUri::YubikeyPiv(key) => { key_uri.push_str(&key.key_name); key_uri.push_str(":piv/"); key_uri.push_str(key.algorithm.to_str()); @@ -64,7 +65,7 @@ impl ToString for KeyUri { key_uri.push_str(key.slot.to_str()); } // key://-:soft/p256::hmac_enc:... - KeyUri::YubikeyHmacEncSoftKey(key) => { + KeyUri::YubikeyHmacEncSoft(key) => { key_uri.push_str(&key.key_name); key_uri.push_str(":soft/"); key_uri.push_str(key.algorithm.to_str()); @@ -72,7 +73,7 @@ impl ToString for KeyUri { key_uri.push_str(key.hmac_enc_private_key.as_str()); } // key://external-command-file-name:external_command/p256::parameter - KeyUri::ExternalCommandKey(key) => { + KeyUri::ExternalCommand(key) => { let encoded_external_command = percent_encoding::utf8_percent_encode(&key.external_command, NON_ALPHANUMERIC) .to_string(); @@ -140,7 +141,7 @@ impl KeyAlgorithmId { } } - pub fn to_jwa_name(&self) -> &str { + pub fn as_jwa_name(&self) -> &str { match self { KeyAlgorithmId::Rsa1024 | KeyAlgorithmId::Rsa2048 @@ -203,6 +204,7 @@ impl KeyUsage { } } +#[allow(clippy::to_string_trait_impl)] impl ToString for KeyUsage { fn to_string(&self) -> String { match self { @@ -269,7 +271,7 @@ pub fn parse_key_uri(key_uri: &str) -> XResult { } Some(key_usage) => key_usage, }; - let parsed_key_uri = KeyUri::SecureEnclaveKey(SecureEnclaveKey { + let parsed_key_uri = KeyUri::SecureEnclave(SecureEnclaveKey { host: host_or_name.to_string(), usage: key_usage, private_key: left_part.to_string(), @@ -278,7 +280,7 @@ pub fn parse_key_uri(key_uri: &str) -> XResult { Ok(parsed_key_uri) } "piv" => { - if "" != usage { + if !usage.is_empty() { return simple_error!("Key uri's usage must be empty."); } let algorithm = opt_value_result!( @@ -291,7 +293,7 @@ pub fn parse_key_uri(key_uri: &str) -> XResult { "Invalid slot id: {}", left_part ); - let parsed_key_uri = KeyUri::YubikeyPivKey(YubikeyPivKey { + let parsed_key_uri = KeyUri::YubikeyPiv(YubikeyPivKey { key_name: host_or_name.to_string(), algorithm, slot, @@ -300,7 +302,7 @@ pub fn parse_key_uri(key_uri: &str) -> XResult { Ok(parsed_key_uri) } "soft" => { - if "" != usage { + if !usage.is_empty() { return simple_error!("Key uri's usage must be empty."); } let algorithm = opt_value_result!( @@ -309,7 +311,7 @@ pub fn parse_key_uri(key_uri: &str) -> XResult { algorithm ); let hmac_enc_private_key = left_part.to_string(); - let parsed_key_uri = KeyUri::YubikeyHmacEncSoftKey(YubikeyHmacEncSoftKey { + let parsed_key_uri = KeyUri::YubikeyHmacEncSoft(YubikeyHmacEncSoftKey { key_name: host_or_name.to_string(), algorithm, hmac_enc_private_key, @@ -318,7 +320,7 @@ pub fn parse_key_uri(key_uri: &str) -> XResult { Ok(parsed_key_uri) } "external_command" => { - if "" != usage { + if !usage.is_empty() { return simple_error!("Key uri's usage must be empty."); } let external_command = opt_result!( @@ -331,7 +333,7 @@ pub fn parse_key_uri(key_uri: &str) -> XResult { algorithm ); let parameter = left_part.to_string(); - let parsed_key_uri = KeyUri::ExternalCommandKey(ExternalCommandKey { + let parsed_key_uri = KeyUri::ExternalCommand(ExternalCommandKey { external_command: external_command.to_string(), algorithm, parameter, @@ -352,7 +354,7 @@ fn test_parse_key_uri_01() { se_key_uri.to_string() ); match se_key_uri { - KeyUri::SecureEnclaveKey(se_key_uri) => { + KeyUri::SecureEnclave(se_key_uri) => { assert_eq!("hatter-mac-pro", se_key_uri.host); assert_eq!(KeyUsage::Singing, se_key_uri.usage); assert_eq!("BASE64(dataRepresentation)", se_key_uri.private_key); @@ -373,7 +375,7 @@ fn test_parse_key_uri_02() { se_key_uri.to_string() ); match se_key_uri { - KeyUri::SecureEnclaveKey(se_key_uri) => { + KeyUri::SecureEnclave(se_key_uri) => { assert_eq!("hatter-mac-m1", se_key_uri.host); assert_eq!(KeyUsage::KeyAgreement, se_key_uri.usage); assert_eq!("BASE64(dataRepresentation)", se_key_uri.private_key); @@ -392,7 +394,7 @@ fn test_parse_key_uri_03() { se_key_uri.to_string() ); match se_key_uri { - KeyUri::YubikeyPivKey(piv_key_uri) => { + KeyUri::YubikeyPiv(piv_key_uri) => { assert_eq!("yubikey-5n", piv_key_uri.key_name); assert_eq!(KeyAlgorithmId::EccP256, piv_key_uri.algorithm); assert_eq!(SlotId::Authentication, piv_key_uri.slot); diff --git a/src/pbeutil.rs b/src/pbeutil.rs index 799a658..1a5b561 100644 --- a/src/pbeutil.rs +++ b/src/pbeutil.rs @@ -94,16 +94,26 @@ pub fn is_simple_pbe_encrypted(ciphertext: &str) -> bool { fn simple_pbe_kdf(password: &str, pbe_salt: &[u8], iteration: u32) -> XResult<[u8; 32]> { let mut init_data = password.as_bytes().to_vec(); - init_data.extend_from_slice(&pbe_salt); + init_data.extend_from_slice(pbe_salt); let mut loop_hash = sha256_bytes(&init_data); for i in 0..iteration { let i_to_bytes = i.to_be_bytes(); - for x in 0..4 { - loop_hash[x] = i_to_bytes[x]; - } + loop_hash[..4].copy_from_slice(&i_to_bytes); loop_hash = sha256_bytes(&loop_hash); } let key = copy_sha256(&sha256_bytes(&loop_hash))?; Ok(key) } + +#[test] +fn test_simple_pbe_kdf() { + assert_eq!("cea7eb6424132aa4d6b1880c3cb570df17c41d766826e144088fd16b334c80c5", + hex::encode(simple_pbe_kdf("hello_world", b"salt", 1).unwrap())); + assert_eq!("e3ed4a0a2f451180cfa4a0f8f3e1181d8863fc192d161e4c1480e3612135ca27", + hex::encode(simple_pbe_kdf("hello_world", b"salt", 2).unwrap())); + assert_eq!("e552900ad3f14a96629c8056bd7bc4b2431250ef4a47e78856626f45807cd87e", + hex::encode(simple_pbe_kdf("hello_world 2", b"salt", 2).unwrap())); + assert_eq!("37e71484f00033c99db444c77553364b31614d5145e38aad5bd5caa6676d40f9", + hex::encode(simple_pbe_kdf("hello_world", b"salt 2", 2).unwrap())); +} diff --git a/src/rsautil.rs b/src/rsautil.rs index f365b3d..673ec05 100644 --- a/src/rsautil.rs +++ b/src/rsautil.rs @@ -36,15 +36,15 @@ impl RsaSignAlgorithm { pub fn sign(rsa_private_key: &RsaPrivateKey, rsa_sign_algorithm: RsaSignAlgorithm, message: &[u8]) -> XResult> { match rsa_sign_algorithm { RsaSignAlgorithm::Rs256 => { - let raw_in = digestutil::sha256_bytes(&message); + let raw_in = digestutil::sha256_bytes(message); Ok(rsa_private_key.sign(Pkcs1v15Sign::new::(), &raw_in)?) } RsaSignAlgorithm::Rs384 => { - let raw_in = digestutil::sha384_bytes(&message); + let raw_in = digestutil::sha384_bytes(message); Ok(rsa_private_key.sign(Pkcs1v15Sign::new::(), &raw_in)?) } RsaSignAlgorithm::Rs512 => { - let raw_in = digestutil::sha512_bytes(&message); + let raw_in = digestutil::sha512_bytes(message); Ok(rsa_private_key.sign(Pkcs1v15Sign::new::(), &raw_in)?) } }