From d4b9b852c1a0dd6cb146bc37e9a7dcda38098b68 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 16 Jun 2024 01:05:15 +0800 Subject: [PATCH] feat: v1.9.5 --- Cargo.lock | 2 +- Cargo.toml | 2 +- README.md | 2 +- src/cmd_chall.rs | 4 +- src/cmd_pivecdh.rs | 98 ++-------------------------------------------- src/ecdhutil.rs | 38 ++++++++++++++++++ src/main.rs | 1 + 7 files changed, 48 insertions(+), 99 deletions(-) create mode 100644 src/ecdhutil.rs diff --git a/Cargo.lock b/Cargo.lock index 57805b8..28022bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -368,7 +368,7 @@ dependencies = [ [[package]] name = "card-cli" -version = "1.9.4" +version = "1.9.5" dependencies = [ "authenticator", "base64 0.21.7", diff --git a/Cargo.toml b/Cargo.toml index dd0bd0c..0078350 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "card-cli" -version = "1.9.4" +version = "1.9.5" authors = ["Hatter Jiang "] edition = "2018" diff --git a/README.md b/README.md index 83c778c..b188e8b 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ vjG+EPEF3g8ywKaS8mZQX+sCAwEAAQ== # piv-ecdh ```shell -$ card-cli piv-ecdh --public --public-key-point-hex 04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0 --json +$ card-cli piv-ecdh --public-256 --public-key-point-hex 04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0 --json { "epk_point_hex": "04bbb6a458e81d2c646587118abfb029ff715db366f92a1d0468887f9947f176c11961eccebd5b9cbbb8b67e33fa8d3f0010a4aaf5010d0f419f1f99b4c2d7aa56", "pk_point_hex": "04dd3eebd906c9cf00b08ec29f7ed61804d1cc1d1352d9257b628191e08fc3717c4fae3298cd5c4829cec8bf3a946e7db60b7857e1287f6a0bae6b3f2342f007d0", diff --git a/src/cmd_chall.rs b/src/cmd_chall.rs index d73f046..2174079 100644 --- a/src/cmd_chall.rs +++ b/src/cmd_chall.rs @@ -19,8 +19,8 @@ impl Command for CommandImpl { .arg(Arg::with_name("challenge-hex").short("x").long("challenge-hex").takes_value(true).help("Challenge HEX")) .arg(Arg::with_name("sha1").short("1").long("sha1").help("Output SHA1")) .arg(Arg::with_name("sha256").short("2").long("sha256").help("Output SHA256")) - .arg(Arg::with_name("sha384").short("3").long("sha384").help("Output SHA256")) - .arg(Arg::with_name("sha512").short("5").long("sha512").help("Output SHA256")) + .arg(Arg::with_name("sha384").short("3").long("sha384").help("Output SHA384")) + .arg(Arg::with_name("sha512").short("5").long("sha512").help("Output SHA512")) .arg(Arg::with_name("json").long("json").help("JSON output")) } diff --git a/src/cmd_pivecdh.rs b/src/cmd_pivecdh.rs index 8326eb6..42a0fce 100644 --- a/src/cmd_pivecdh.rs +++ b/src/cmd_pivecdh.rs @@ -2,17 +2,13 @@ use std::collections::BTreeMap; use std::fs; use clap::{App, Arg, ArgMatches, SubCommand}; -use p256::{EncodedPoint as P256EncodedPoint, PublicKey as P256PublicKey}; -use p256::ecdh::EphemeralSecret as P256EphemeralSecret; -use p384::{EncodedPoint as P384EncodedPoint, PublicKey as P384PublicKey}; -use p384::ecdh::EphemeralSecret as P384EphemeralSecret; use rand::rngs::OsRng; use rust_util::util_clap::{Command, CommandError}; use rust_util::util_msg; use yubikey::{PinPolicy, YubiKey}; use yubikey::piv::{AlgorithmId, decrypt_data, metadata}; -use crate::pivutil; +use crate::{ecdhutil, pivutil}; use crate::pivutil::get_algorithm_id; pub struct CommandImpl; @@ -67,61 +63,9 @@ impl Command for CommandImpl { } if public256 { - use p256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; - let public_key; - if let Some(public_key_pem) = public_key_pem_opt { - public_key = opt_result!(public_key_pem.parse::(), "Parse public key failed: {}"); - } else { - let public_key_point_hex = sub_arg_matches.value_of("public-key-point-hex").unwrap_or_else(|| - failure_and_exit!("--public-key, --public-key-file or --public-key-point-hex must require one")); - let public_key_point_bytes = opt_result!(hex::decode(public_key_point_hex), "Parse public key point hex failed: {}"); - let encoded_point = opt_result!(P256EncodedPoint::from_bytes(public_key_point_bytes), "Parse public key point failed: {}"); - public_key = P256PublicKey::from_encoded_point(&encoded_point).unwrap(); - }; - let esk = P256EphemeralSecret::random(&mut OsRng); - let epk = esk.public_key(); - let epk_bytes = P256EphemeralKeyBytes::from_public_key(&epk); - - let public_key_encoded_point = public_key.to_encoded_point(false); - - let shared_secret = esk.diffie_hellman(&public_key); - if json_output { - json.insert("shared_secret_hex", hex::encode(shared_secret.raw_secret_bytes())); - json.insert("epk_point_hex", hex::encode(epk_bytes.decompress().as_bytes())); - json.insert("pk_point_hex", hex::encode(public_key_encoded_point.as_bytes())); - } else { - information!("Shared secret: {}", hex::encode(shared_secret.raw_secret_bytes())); - information!("EPK point: {}", hex::encode(epk_bytes.decompress().as_bytes())); - information!("Public key point: {}", hex::encode(public_key_encoded_point.as_bytes())); - } + ecdhutil::piv_ecdh!(p256, public_key_pem_opt, sub_arg_matches, json, json_output); } else { - use p384::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; - let public_key; - if let Some(public_key_pem) = public_key_pem_opt { - public_key = opt_result!(public_key_pem.parse::(), "Parse public key failed: {}"); - } else { - let public_key_point_hex = sub_arg_matches.value_of("public-key-point-hex").unwrap_or_else(|| - failure_and_exit!("--public-key, --public-key-file or --public-key-point-hex must require one")); - let public_key_point_bytes = opt_result!(hex::decode(public_key_point_hex), "Parse public key point hex failed: {}"); - let encoded_point = opt_result!(P384EncodedPoint::from_bytes(public_key_point_bytes), "Parse public key point failed: {}"); - public_key = P384PublicKey::from_encoded_point(&encoded_point).unwrap(); - }; - let esk = P384EphemeralSecret::random(&mut OsRng); - let epk = esk.public_key(); - let epk_bytes = P384EphemeralKeyBytes::from_public_key(&epk); - - let public_key_encoded_point = public_key.to_encoded_point(false); - - let shared_secret = esk.diffie_hellman(&public_key); - if json_output { - json.insert("shared_secret_hex", hex::encode(shared_secret.raw_secret_bytes())); - json.insert("epk_point_hex", hex::encode(epk_bytes.decompress().as_bytes())); - json.insert("pk_point_hex", hex::encode(public_key_encoded_point.as_bytes())); - } else { - information!("Shared secret: {}", hex::encode(shared_secret.raw_secret_bytes())); - information!("EPK point: {}", hex::encode(epk_bytes.decompress().as_bytes())); - information!("Public key point: {}", hex::encode(public_key_encoded_point.as_bytes())); - } + ecdhutil::piv_ecdh!(p384, public_key_pem_opt, sub_arg_matches, json, json_output); } } @@ -166,7 +110,7 @@ impl Command for CommandImpl { } let epk_bytes = opt_result!(hex::decode(epk), "Parse epk failed: {}"); - let epk_bits = (epk_bytes.len() - 1) * 8; + let epk_bits = ((epk_bytes.len() - 1) / 2) * 8; debugging!("Epk {} bits", epk_bits); let decrypted_shared_secret = opt_result!(decrypt_data( &mut yk, @@ -188,37 +132,3 @@ impl Command for CommandImpl { Ok(None) } } - -#[derive(Debug)] -pub struct P256EphemeralKeyBytes(P256EncodedPoint); - -impl P256EphemeralKeyBytes { - fn from_public_key(epk: &P256PublicKey) -> Self { - use p256::elliptic_curve::sec1::ToEncodedPoint; - P256EphemeralKeyBytes(epk.to_encoded_point(true)) - } - - fn decompress(&self) -> P256EncodedPoint { - // EphemeralKeyBytes is a valid-compressed encoding by construction. - use p256::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; - let p = P256PublicKey::from_encoded_point(&self.0).unwrap(); - p.to_encoded_point(false) - } -} - -#[derive(Debug)] -pub struct P384EphemeralKeyBytes(P384EncodedPoint); - -impl P384EphemeralKeyBytes { - fn from_public_key(epk: &P384PublicKey) -> Self { - use p384::elliptic_curve::sec1::ToEncodedPoint; - P384EphemeralKeyBytes(epk.to_encoded_point(true)) - } - - fn decompress(&self) -> P384EncodedPoint { - // EphemeralKeyBytes is a valid-compressed encoding by construction. - use p384::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; - let p = P384PublicKey::from_encoded_point(&self.0).unwrap(); - p.to_encoded_point(false) - } -} diff --git a/src/ecdhutil.rs b/src/ecdhutil.rs new file mode 100644 index 0000000..a98cf61 --- /dev/null +++ b/src/ecdhutil.rs @@ -0,0 +1,38 @@ + +macro_rules! piv_ecdh { + ($p_algo: tt, $public_key_pem_opt: expr, $sub_arg_matches: expr, $json: expr, $json_output: expr) => ({ + use $p_algo::elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}; + use $p_algo::ecdh::EphemeralSecret; + use $p_algo::{EncodedPoint, PublicKey}; + let public_key; + if let Some(public_key_pem) = $public_key_pem_opt { + public_key = opt_result!(public_key_pem.parse::(), "Parse public key failed: {}"); + } else { + let public_key_point_hex = $sub_arg_matches.value_of("public-key-point-hex").unwrap_or_else(|| + failure_and_exit!("--public-key, --public-key-file or --public-key-point-hex must require one")); + let public_key_point_bytes = opt_result!(hex::decode(public_key_point_hex), "Parse public key point hex failed: {}"); + let encoded_point = opt_result!(EncodedPoint::from_bytes(public_key_point_bytes), "Parse public key point failed: {}"); + public_key = PublicKey::from_encoded_point(&encoded_point).unwrap(); + }; + let public_key_encoded_point = public_key.to_encoded_point(false); + + let esk = EphemeralSecret::random(&mut OsRng); + let epk = esk.public_key(); + let epk_compress_point = epk.to_encoded_point(true); + let epk_point = PublicKey::from_encoded_point(&epk_compress_point).unwrap(); + let epk_uncompressed_point = epk_point.to_encoded_point(false); + + let shared_secret = esk.diffie_hellman(&public_key); + if $json_output { + $json.insert("shared_secret_hex", hex::encode(shared_secret.raw_secret_bytes())); + $json.insert("epk_point_hex", hex::encode(epk_uncompressed_point.as_bytes())); + $json.insert("pk_point_hex", hex::encode(public_key_encoded_point.as_bytes())); + } else { + information!("Shared secret: {}", hex::encode(shared_secret.raw_secret_bytes())); + information!("EPK point: {}", hex::encode(epk_uncompressed_point.as_bytes())); + information!("Public key point: {}", hex::encode(public_key_encoded_point.as_bytes())); + } + }) +} + +pub(crate) use piv_ecdh; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 0e93fcc..c6a4eec 100644 --- a/src/main.rs +++ b/src/main.rs @@ -45,6 +45,7 @@ mod cmd_signjwt; mod cmd_signfile; mod cmd_verifyfile; mod signfile; +mod ecdhutil; pub struct DefaultCommandImpl;