From 2f0d4d6e8a52fd03f225d3b4f6f1357768c49cd7 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Tue, 12 Apr 2022 00:29:10 +0800 Subject: [PATCH] feat: v1.1.15, supports u2f-sign --json --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cmd_u2fsign.rs | 111 +++++++++++++++++++++++++++------------------ 3 files changed, 69 insertions(+), 46 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e34b027..95f66e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -384,7 +384,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.1.14" +version = "1.1.15" dependencies = [ "authenticator", "base64 0.13.0", diff --git a/Cargo.toml b/Cargo.toml index 22f0065..92c7536 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.1.14" +version = "1.1.15" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_u2fsign.rs b/src/cmd_u2fsign.rs index caf770a..8243dfa 100644 --- a/src/cmd_u2fsign.rs +++ b/src/cmd_u2fsign.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeMap; use std::sync::mpsc::channel; use authenticator::{AuthenticatorTransports, KeyHandle, SignFlags}; @@ -28,8 +29,12 @@ impl Command for CommandImpl { .arg(Arg::with_name("public-key-hex").long("public-key-hex").takes_value(true).help("Public key hex")) .arg(Arg::with_name("challenge").long("challenge").takes_value(true).help("Challenge HEX")) .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")) } 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 app_id = sub_arg_matches.value_of("app-id").unwrap(); let timeout_ms = match sub_arg_matches.value_of("timeout").unwrap().parse::() { Ok(t) => (t * 1000) as u64, @@ -75,8 +80,8 @@ impl Command for CommandImpl { if let Err(e) = manager.sign( flags, timeout_ms, - chall_bytes, - vec![app_bytes], + chall_bytes.clone(), + vec![app_bytes.clone()], request_key_handles, status_tx, callback, @@ -91,55 +96,73 @@ impl Command for CommandImpl { let counter = &sign_data[1..=4]; let signature = &sign_data[5..]; + let client_data = u2fv2_challenge_str.as_bytes().to_vec(); + let app_id_hash = sha256(app_id.as_bytes()); + let client_data_hash = sha256(&client_data[..]); + let mut signed_message = Vec::with_capacity(128); + signed_message.extend_from_slice(&app_id_hash); + signed_message.push(*user_presence_flag); + signed_message.extend_from_slice(counter); + signed_message.extend_from_slice(&client_data_hash); + success!("Device info: {}", &device_info); - information!("Sign challenge: {}", u2fv2_challenge_str); - information!("Sign challenge base64: {}", base64::encode(&u2fv2_challenge_str)); - information!("Sign result : {}", base64::encode(&sign_data)); - information!("- presence : {}", user_presence_flag); - information!("- counter : {}", u32::from_be_bytes([counter[0], counter[1], counter[2], counter[3]])); - information!("- signature: {}", hex::encode(&signature)); - // success!("Key handle used: {}", base64::encode(&handle_used)); - information!("Key handle: {}", hex::encode(&handle_used)); + let mut json = BTreeMap::new(); + if json_output { + json.insert("app_id", app_id.to_string()); + json.insert("app_id_hash", hex::encode(&app_bytes)); + json.insert("challenge", u2fv2_challenge_str.to_string()); + json.insert("challenge_hash", hex::encode(&chall_bytes)); + json.insert("device_info", format!("{}", &device_info)); + json.insert("signature", hex::encode(&signature)); + json.insert("signed_message", hex::encode(&signed_message)); + json.insert("key_handle", hex::encode(&handle_used)); + json.insert("sign_data", hex::encode(&sign_data)); + } else { + information!("Sign challenge: {}", u2fv2_challenge_str); + information!("Sign challenge base64: {}", base64::encode(&u2fv2_challenge_str)); + information!("Sign result : {}", base64::encode(&sign_data)); + information!("- presence : {}", user_presence_flag); + information!("- counter : {}", u32::from_be_bytes([counter[0], counter[1], counter[2], counter[3]])); + information!("- signature: {}", hex::encode(&signature)); + // success!("Key handle used: {}", base64::encode(&handle_used)); + information!("Key handle: {}", hex::encode(&handle_used)); + } if let Some(public_key_hex) = sub_arg_matches.value_of("public-key-hex") { let public_key = opt_result!(hex::decode(public_key_hex), "Parse public key hex failed: {}"); - - let client_data = u2fv2_challenge_str.as_bytes().to_vec(); - let app_id_hash = sha256(app_id.as_bytes()); - let client_data_hash = sha256(&client_data[..]); - let mut signed_message = Vec::with_capacity(128); - signed_message.extend_from_slice(&app_id_hash); - signed_message.push(*user_presence_flag); - signed_message.extend_from_slice(counter); - signed_message.extend_from_slice(&client_data_hash); - - information!("Public key: {}", hex::encode(&public_key)); - information!("Signed message: {}", hex::encode(&signed_message)); - - let authorization_result = u2f::authorization::parse_sign_response( - app_id.to_string(), - client_data, - public_key.clone(), - sign_data.clone(), - ); - let authorization = opt_result!(authorization_result, "Parse authorization failed: {}"); - success!("Parse authorization success, counter: {}", authorization.counter); - - // PKey::public_key_from_der() - let ec_group = opt_result!(EcGroup::from_curve_name(Nid::X9_62_PRIME256V1), "New secp256r1 EC group failed: {}"); - let ec_point = opt_result!(EcPoint::from_bytes(&ec_group, &public_key, &mut BigNumContext::new().unwrap()), "Parse from secp256r1 point failed: {}"); - let ec_key = opt_result!(EcKey::from_public_key(&ec_group, &ec_point), "Parse secp256r1 public key failed: {}"); - let ec_pkey = opt_result!(PKey::from_ec_key(ec_key), "EC secp256r1 key to PKey failed: {}"); - let mut verifier = opt_result!(Verifier::new(MessageDigest::sha256(), &ec_pkey), "Verifier new failed: {}"); - verifier.update(&signed_message)?; - let verify_result = opt_result!(verifier.verify(signature), "Verifier verify failed: {}"); - if verify_result { - success!("Verify success"); + if json_output { + json.insert("pub_key", hex::encode(&public_key)); } else { - failure!("Verify failed"); + information!("Public key: {}", hex::encode(&public_key)); + information!("Signed message: {}", hex::encode(&signed_message)); + + let authorization_result = u2f::authorization::parse_sign_response( + app_id.to_string(), + client_data, + public_key.clone(), + sign_data.clone(), + ); + let authorization = opt_result!(authorization_result, "Parse authorization failed: {}"); + success!("Parse authorization success, counter: {}", authorization.counter); + + // PKey::public_key_from_der() + let ec_group = opt_result!(EcGroup::from_curve_name(Nid::X9_62_PRIME256V1), "New secp256r1 EC group failed: {}"); + let ec_point = opt_result!(EcPoint::from_bytes(&ec_group, &public_key, &mut BigNumContext::new().unwrap()), "Parse from secp256r1 point failed: {}"); + let ec_key = opt_result!(EcKey::from_public_key(&ec_group, &ec_point), "Parse secp256r1 public key failed: {}"); + let ec_pkey = opt_result!(PKey::from_ec_key(ec_key), "EC secp256r1 key to PKey failed: {}"); + let mut verifier = opt_result!(Verifier::new(MessageDigest::sha256(), &ec_pkey), "Verifier new failed: {}"); + verifier.update(&signed_message)?; + let verify_result = opt_result!(verifier.verify(signature), "Verifier verify failed: {}"); + if verify_result { + success!("Verify success"); + } else { + failure!("Verify failed"); + } } } - + if json_output { + println!("{}", serde_json::to_string_pretty(&json).unwrap()); + } Ok(None) } } \ No newline at end of file