feat: v1.10.14, add --no-pin for many subcommands
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -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",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "card-cli"
|
||||
version = "1.10.13"
|
||||
version = "1.10.14"
|
||||
authors = ["Hatter Jiang <jht5945@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
8
justfile
8
justfile
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: {}");
|
||||
}
|
||||
|
||||
|
||||
@@ -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)?;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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: {}");
|
||||
}
|
||||
|
||||
@@ -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: {}");
|
||||
|
||||
|
||||
@@ -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: {}");
|
||||
|
||||
@@ -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![];
|
||||
|
||||
@@ -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());
|
||||
|
||||
Reference in New Issue
Block a user