feat: v1.10.14, add --no-pin for many subcommands

This commit is contained in:
2024-12-29 11:54:25 +08:00
parent ef1f637c83
commit 5a1942e150
12 changed files with 47 additions and 41 deletions

2
Cargo.lock generated
View File

@@ -487,7 +487,7 @@ dependencies = [
[[package]]
name = "card-cli"
version = "1.10.13"
version = "1.10.14"
dependencies = [
"authenticator 0.3.1",
"base64 0.21.7",

View File

@@ -1,6 +1,6 @@
[package]
name = "card-cli"
version = "1.10.13"
version = "1.10.14"
authors = ["Hatter Jiang <jht5945@gmail.com>"]
edition = "2018"

View File

@@ -5,6 +5,14 @@ _:
install:
cargo install --path .
# build without default features
build-simple:
cargo build --no-default-features
# install without default features
install-simple:
cargo install --no-default-features
# run --help
help:
cargo r -- --help

View File

@@ -18,6 +18,7 @@ impl Command for CommandImpl {
SubCommand::with_name(self.name()).about("PIV decrypt(RSA) subcommand")
.arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"))
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN"))
.arg(Arg::with_name("no-pin").long("no-pin").help("No PIN"))
.arg(Arg::with_name("ciphertext").long("ciphertext").short("c").takes_value(true).help("Encrypted data (HEX or Base64)"))
.arg(Arg::with_name("stdin").long("stdin").help("Standard input (Ciphertext)"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
@@ -29,10 +30,7 @@ impl Command for CommandImpl {
let slot = opt_value_result!(sub_arg_matches.value_of("slot"), "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e");
let pin_opt = sub_arg_matches.value_of("pin");
let pin_opt = pinutil::get_pin(pin_opt);
let pin_opt = pin_opt.as_deref();
let pin = opt_value_result!(pin_opt, "User pin must be assigned");
let pin_opt = pinutil::read_pin(sub_arg_matches);
let encrypted_data = if let Some(ciphertext) = sub_arg_matches.value_of("ciphertext") {
opt_result!(try_decode(ciphertext), "Decode --ciphertext failed: {}")
@@ -43,7 +41,9 @@ impl Command for CommandImpl {
};
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
if let Some(pin) = &pin_opt {
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
}
let slot_id = pivutil::get_slot_id(slot)?;
let decrypt_result = yubikey::piv::decrypt_data(&mut yk, &encrypted_data,

View File

@@ -19,6 +19,7 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV ECDH subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN"))
.arg(Arg::with_name("no-pin").long("no-pin").help("No PIN"))
.arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ..."))
.arg(Arg::with_name("public-256").long("public-256").help("Public key (P-256)"))
.arg(Arg::with_name("public-384").long("public-384").help("Public key (P-384)"))
@@ -70,9 +71,7 @@ impl Command for CommandImpl {
}
if private {
let pin_opt = sub_arg_matches.value_of("pin");
let pin_opt = pinutil::get_pin(pin_opt);
let pin_opt = pin_opt.as_deref();
let pin_opt = pinutil::read_pin(sub_arg_matches);
let slot = opt_value_result!(sub_arg_matches.value_of("slot"), "--slot must assigned, e.g. 82, 83 ...");
let epk = opt_value_result!(sub_arg_matches.value_of("epk"), "--epk must assigned");
@@ -107,7 +106,7 @@ impl Command for CommandImpl {
warning!("Get slot: {} meta data failed", slot);
}
if let Some(pin) = pin_opt {
if let Some(pin) = &pin_opt {
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
}

View File

@@ -4,11 +4,11 @@ use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use x509_parser::nom::AsBytes;
use yubikey::piv::{AlgorithmId, ManagementAlgorithmId, metadata, sign_data};
use yubikey::piv::{metadata, sign_data, AlgorithmId, ManagementAlgorithmId};
use yubikey::YubiKey;
use crate::{argsutil, pinutil, pivutil};
use crate::util::base64_encode;
use crate::{argsutil, pinutil, pivutil};
pub struct CommandImpl;
@@ -33,14 +33,7 @@ impl Command for CommandImpl {
let mut json = BTreeMap::<&'_ str, String>::new();
let pin_opt = if sub_arg_matches.is_present("no-pin") {
None
} else {
let pin_opt = sub_arg_matches.value_of("pin");
let pin_opt = pinutil::get_pin(pin_opt);
let pin_opt = pin_opt.as_deref();
pin_opt.map(|p| p.to_string())
};
let pin_opt = pinutil::read_pin(sub_arg_matches);
let slot = opt_value_result!(sub_arg_matches.value_of("slot"), "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e");
let hash_bytes = argsutil::get_sha256_digest_or_hash(sub_arg_matches)?;

View File

@@ -18,6 +18,7 @@ impl Command for CommandImpl {
SubCommand::with_name(self.name()).about("PIV RSA sign(with SHA256) subcommand")
.arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"))
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN"))
.arg(Arg::with_name("no-pin").long("no-pin").help("No PIN"))
.arg(Arg::with_name("sha256").short("2").long("sha256").takes_value(true).help("Digest SHA256 HEX"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
}
@@ -26,15 +27,14 @@ impl Command for CommandImpl {
let json_output = sub_arg_matches.is_present("json");
if json_output { util_msg::set_logger_std_out(false); }
let pin_opt = sub_arg_matches.value_of("pin");
let pin_opt = pinutil::get_pin(pin_opt);
let pin_opt = pin_opt.as_deref();
let pin = opt_value_result!(pin_opt, "User pin must be assigned");
let pin_opt = pinutil::read_pin(sub_arg_matches);
let sha256_hex_opt = sub_arg_matches.value_of("sha256").map(|s| s.to_string());
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
if let Some(pin) = &pin_opt {
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
}
let slot_id = match sub_arg_matches.value_of("slot") {
None => SlotId::Signature,

View File

@@ -41,6 +41,7 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV sign(with SHA256) subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN"))
.arg(Arg::with_name("no-pin").long("no-pin").help("No PIN"))
.arg(Arg::with_name("slot").short("s").long("slot")
.takes_value(true).required(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"))
.arg(Arg::with_name("file").short("f").long("file").takes_value(true).required(true).help("Input file"))
@@ -57,9 +58,7 @@ impl Command for CommandImpl {
let comment_opt = sub_arg_matches.value_of("comment").map(ToString::to_string);
let attributes_opt = sub_arg_matches.value_of("attributes").map(ToString::to_string);
let pin_opt = sub_arg_matches.value_of("pin");
let pin_opt = pinutil::get_pin(pin_opt);
let pin_opt = pin_opt.as_deref();
let pin_opt = pinutil::read_pin(sub_arg_matches);
let slot = opt_value_result!(sub_arg_matches.value_of("slot"), "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e");
// TODO read from stream not in memory
@@ -83,7 +82,7 @@ impl Command for CommandImpl {
let algorithm_id = opt_result!(
pivutil::get_algorithm_id_by_certificate(certificate), "Get slot key algorithm failed: {}");
debugging!("PIV algorithm: {:?}", algorithm_id);
if let Some(pin) = pin_opt {
if let Some(pin) = &pin_opt {
debugging!("PIN is assigned.");
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
}

View File

@@ -22,6 +22,7 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("Sign JWT subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN"))
.arg(Arg::with_name("no-pin").long("no-pin").help("No PIN"))
.arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"))
.arg(Arg::with_name("key-id").short("K").long("key-id").takes_value(true).help("Header key ID"))
.arg(Arg::with_name("claims").short("C").long("claims").takes_value(true).multiple(true).help("Claims, key:value"))
@@ -37,9 +38,7 @@ impl Command for CommandImpl {
let mut json = BTreeMap::<&'_ str, String>::new();
let pin_opt = sub_arg_matches.value_of("pin");
let pin_opt = pinutil::get_pin(pin_opt);
let pin_opt = pin_opt.as_deref();
let pin_opt = pinutil::read_pin(sub_arg_matches);
let slot = opt_value_result!(
sub_arg_matches.value_of("slot"), "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e");
@@ -110,7 +109,7 @@ impl Command for CommandImpl {
}
fn sign_jwt(slot: &str, pin_opt: &Option<&str>, mut header: Header, payload: &Option<&str>, claims: &Map<String, Value>) -> XResult<String> {
fn sign_jwt(slot: &str, pin_opt: &Option<String>, mut header: Header, payload: &Option<&str>, claims: &Map<String, Value>) -> XResult<String> {
let mut yk = opt_result!(YubiKey::open(), "Find YubiKey failed: {}");
let slot_id = opt_result!(pivutil::get_slot_id(slot), "Get slot id failed: {}");

View File

@@ -28,6 +28,7 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("SSH PIV sign cert subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN"))
.arg(Arg::with_name("no-pin").long("no-pin").help("No PIN"))
.arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"))
.arg(Arg::with_name("key-id").short("k").long("key-id").takes_value(true).default_value("default_key_id").help("SSH user CA key id"))
.arg(Arg::with_name("principal").short("P").long("principal").takes_value(true).default_value("root").multiple(true).help("SSH user CA principal"))
@@ -85,8 +86,7 @@ impl Command for CommandImpl {
information!("Principals: {:?}", principals);
information!("Validity: {} seconds", validity_u64);
let pin_opt = sub_arg_matches.value_of("pin");
let pin_opt = pinutil::get_pin(pin_opt);
let pin_opt = pinutil::read_pin(sub_arg_matches);
let cert_der = find_cert(&mut yk, slot_id)?;
let ca_ssh_pub_key = opt_result!(extract_ssh_pubkey_from_x509_certificate(&cert_der), "Extract SSH public key failed: {}");

View File

@@ -18,6 +18,7 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("SSH piv sign subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN"))
.arg(Arg::with_name("no-pin").long("no-pin").help("No PIN"))
.arg(Arg::with_name("slot").short("s").long("slot").takes_value(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"))
.arg(Arg::with_name("namespace").short("n").long("namespace").takes_value(true).help("Namespace"))
.arg(Arg::with_name("in").long("in").required(true).takes_value(true).help("In file, - for stdin"))
@@ -35,9 +36,7 @@ impl Command for CommandImpl {
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");
let slot_id = pivutil::get_slot_id(slot)?;
let pin_opt = sub_arg_matches.value_of("pin");
let pin_opt = pinutil::get_pin(pin_opt);
let pin_opt = pin_opt.as_deref();
let pin_opt = pinutil::read_pin(sub_arg_matches);
let mut algorithm_id_opt = None;
let mut ec_key_point = vec![];
@@ -96,7 +95,7 @@ impl Command for CommandImpl {
crate::digest::sha384_bytes(&sign_message)
};
if let Some(pin) = pin_opt {
if let Some(pin) = &pin_opt {
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
}
let mut signature_value = vec![];

View File

@@ -1,5 +1,5 @@
use std::{env, fs};
use clap::ArgMatches;
use pinentry::PassphraseInput;
use secrecy::ExposeSecret;
@@ -7,6 +7,15 @@ const PIN_ENTRY_ENV: &str = "PIN_ENTRY_CMD";
const PIN_ENTRY_1: &str = "/usr/local/MacGPG2/libexec/pinentry-mac.app/Contents/MacOS/pinentry-mac";
const PIN_ENTRY_DEFAULT: &str = "pinentry";
pub fn read_pin(sub_arg_matches: &ArgMatches) -> Option<String> {
if sub_arg_matches.is_present("no-pin") {
None
} else {
let pin_opt = sub_arg_matches.value_of("pin");
get_pin(pin_opt)
}
}
pub fn get_pin(pin_opt: Option<&str>) -> Option<String> {
if let Some(pin) = pin_opt {
return Some(pin.to_string());