From 7d2cf85f8988a3c6eaceadcbfc4a90fb3b4e557b Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sat, 22 Mar 2025 10:04:39 +0800 Subject: [PATCH] feat: v1.10.18, add parse-ecdsa-signature --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/cmd_parseecdsasignature.rs | 64 ++++++++++++++++++++++++++++++++++ src/main.rs | 2 ++ src/util.rs | 9 ++++- 5 files changed, 76 insertions(+), 3 deletions(-) create mode 100644 src/cmd_parseecdsasignature.rs diff --git a/Cargo.lock b/Cargo.lock index fb782f9..957cdc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -487,7 +487,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.10.17" +version = "1.10.18" dependencies = [ "authenticator 0.3.1", "base64 0.21.7", diff --git a/Cargo.toml b/Cargo.toml index 0d0dd3d..9cb830e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.10.17" +version = "1.10.18" authors = ["Hatter Jiang "] edition = "2018" diff --git a/src/cmd_parseecdsasignature.rs b/src/cmd_parseecdsasignature.rs new file mode 100644 index 0000000..f428e7e --- /dev/null +++ b/src/cmd_parseecdsasignature.rs @@ -0,0 +1,64 @@ +use std::collections::BTreeMap; + +use clap::{App, Arg, ArgMatches, SubCommand}; + +use rust_util::util_clap::{Command, CommandError}; +use rust_util::util_msg; + +use crate::ecdsautil::parse_ecdsa_r_and_s; +use crate::util::try_decode; + +const SEPARATOR: &str = "."; + +pub struct CommandImpl; + +impl Command for CommandImpl { + fn name(&self) -> &str { + "parse-ecdsa-signature" + } + + fn subcommand<'a>(&self) -> App<'a, 'a> { + SubCommand::with_name(self.name()) + .about("Parse ECDSA signature") + .arg( + Arg::with_name("signature") + .long("signature") + .required(true) + .takes_value(true) + .help("ECDSA signature"), + ) + .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 { + util_msg::set_logger_std_out(false); + } + + let mut json = BTreeMap::<&'_ str, String>::new(); + + let signature = sub_arg_matches.value_of("signature").unwrap(); + + let signature_der = try_decode(signature)?; + + let (r, s) = parse_ecdsa_r_and_s(&signature_der)?; + let mut r_and_s = r.clone(); + r_and_s.extend_from_slice(&s); + + if json_output { + json.insert("r", hex::encode(&r)); + json.insert("s", hex::encode(&s)); + json.insert("rs", hex::encode(&r_and_s)); + } else { + information!("R: {}", hex::encode(&r)); + information!("S: {}", hex::encode(&s)); + information!("RS: {}", hex::encode(&r_and_s)); + } + + if json_output { + println!("{}", serde_json::to_string_pretty(&json).unwrap()); + } + Ok(None) + } +} diff --git a/src/main.rs b/src/main.rs index aaeb244..aaf2811 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,6 +52,7 @@ mod cmd_sshpubkey; mod cmd_u2fregister; mod cmd_u2fsign; mod cmd_verifyfile; +mod cmd_parseecdsasignature; mod digest; mod ecdhutil; mod ecdsautil; @@ -142,6 +143,7 @@ fn inner_main() -> CommandError { #[cfg(feature = "with-secure-enclave")] Box::new(cmd_se_ecdh::CommandImpl), Box::new(cmd_ecverify::CommandImpl), + Box::new(cmd_parseecdsasignature::CommandImpl), ]; #[allow(clippy::vec_init_then_push)] diff --git a/src/util.rs b/src/util.rs index a5ba079..fa32b2f 100644 --- a/src/util.rs +++ b/src/util.rs @@ -17,12 +17,19 @@ pub fn base64_decode>(input: T) -> Result, DecodeError> { STANDARD.decode(input) } +pub fn base64_uri_decode>(input: T) -> Result, DecodeError> { + URL_SAFE_NO_PAD.decode(input) +} + pub fn try_decode(input: &str) -> XResult> { match hex::decode(input) { Ok(v) => Ok(v), Err(_) => match base64_decode(input) { Ok(v) => Ok(v), - Err(e) => simple_error!("decode hex or base64 error: {}", e), + Err(_) => match base64_uri_decode(input) { + Ok(v) => Ok(v), + Err(e) => simple_error!("decode hex or base64 error: {}", e), + }, } } }