diff --git a/justfile b/justfile index 43dabec..7764956 100644 --- a/justfile +++ b/justfile @@ -11,7 +11,7 @@ build-simple: # install without default features install-simple: - cargo install --no-default-features + cargo install --no-default-features --path . # run --help help: diff --git a/src/cmd_pivecsign.rs b/src/cmd_pivecsign.rs index 9a121b7..fabff4d 100644 --- a/src/cmd_pivecsign.rs +++ b/src/cmd_pivecsign.rs @@ -8,7 +8,7 @@ use yubikey::piv::{metadata, sign_data, AlgorithmId, ManagementAlgorithmId}; use yubikey::YubiKey; use crate::util::base64_encode; -use crate::{argsutil, pinutil, pivutil}; +use crate::{argsutil, pivutil}; pub struct CommandImpl; @@ -33,8 +33,6 @@ impl Command for CommandImpl { let mut json = BTreeMap::<&'_ str, String>::new(); - 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)?; let (algorithm, algorithm_str) = match sub_arg_matches.value_of("algorithm") { @@ -45,6 +43,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 = pivutil::check_read_pin(&mut yk, slot_id, sub_arg_matches); if let Some(pin) = &pin_opt { opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}"); diff --git a/src/cmd_signjwt.rs b/src/cmd_signjwt.rs index 3a93ac1..a654677 100644 --- a/src/cmd_signjwt.rs +++ b/src/cmd_signjwt.rs @@ -8,9 +8,9 @@ use rust_util::{util_msg, util_time, XResult}; use rust_util::util_clap::{Command, CommandError}; use serde_json::{Map, Number, Value}; use yubikey::{Certificate, YubiKey}; -use yubikey::piv::{AlgorithmId, sign_data}; +use yubikey::piv::{AlgorithmId, sign_data, SlotId}; -use crate::{digest, pinutil, pivutil, rsautil, util}; +use crate::{digest, pivutil, rsautil, util}; const SEPARATOR: &str = "."; @@ -38,7 +38,6 @@ impl Command for CommandImpl { let mut json = BTreeMap::<&'_ str, String>::new(); - 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"); @@ -97,7 +96,11 @@ impl Command for CommandImpl { } } - let token_string = sign_jwt(slot, &pin_opt, header, &payload, &jwt_claims)?; + let mut yk = opt_result!(YubiKey::open(), "Find YubiKey failed: {}"); + let slot_id = opt_result!(pivutil::get_slot_id(slot), "Get slot id failed: {}"); + let pin_opt = pivutil::check_read_pin(&mut yk, slot_id, sub_arg_matches); + + let token_string = sign_jwt(&mut yk, slot_id, &pin_opt, header, &payload, &jwt_claims)?; success!("Singed JWT: {}", token_string); if json_output { json.insert("token", token_string.clone()); } @@ -109,15 +112,11 @@ impl Command for CommandImpl { } -fn sign_jwt(slot: &str, pin_opt: &Option, mut header: Header, payload: &Option<&str>, claims: &Map) -> XResult { - let mut yk = opt_result!(YubiKey::open(), "Find YubiKey failed: {}"); - let slot_id = opt_result!(pivutil::get_slot_id(slot), "Get slot id failed: {}"); - +fn sign_jwt(yk: &mut YubiKey, slot_id: SlotId, pin_opt: &Option, mut header: Header, payload: &Option<&str>, claims: &Map) -> XResult { if let Some(pin) = pin_opt { opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}"); } - - let cert = match Certificate::read(&mut yk, slot_id) { + let cert = match Certificate::read(yk, slot_id) { Ok(c) => c, Err(e) => return simple_error!("Read YubiKey certificate failed: {}", e), }; @@ -153,7 +152,7 @@ fn sign_jwt(slot: &str, pin_opt: &Option, mut header: Header, payload: & }; let signed_data = opt_result!( - sign_data(&mut yk, &raw_in, yk_algorithm, slot_id), "Sign YubiKey failed: {}"); + sign_data(yk, &raw_in, yk_algorithm, slot_id), "Sign YubiKey failed: {}"); let signature = util::base64_encode_url_safe_no_pad(signed_data); diff --git a/src/pivutil.rs b/src/pivutil.rs index ad3b366..40750f3 100644 --- a/src/pivutil.rs +++ b/src/pivutil.rs @@ -1,11 +1,13 @@ +use clap::ArgMatches; use rust_util::XResult; use spki::{ObjectIdentifier, SubjectPublicKeyInfoOwned}; use spki::der::{Decode, Encode}; use x509_parser::prelude::FromDer; use x509_parser::public_key::RSAPublicKey; -use yubikey::{Certificate, PinPolicy, TouchPolicy}; -use yubikey::piv::{AlgorithmId, ManagementAlgorithmId, ManagementSlotId, Origin, RetiredSlotId}; +use yubikey::{Certificate, PinPolicy, TouchPolicy, YubiKey}; +use yubikey::piv::{metadata, AlgorithmId, ManagementAlgorithmId, ManagementSlotId, Origin, RetiredSlotId}; use yubikey::piv::SlotId; +use crate::pinutil; const RSA: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.1"); const ECC: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1"); @@ -189,4 +191,27 @@ pub fn get_slot_id(slot: &str) -> XResult { "r20" | "95" => SlotId::Retired(RetiredSlotId::R20), _ => return simple_error!("Unknown slot: {}", slot), }) -} \ No newline at end of file +} + +pub fn check_read_pin(yk: &mut YubiKey, slot_id: SlotId, sub_arg_matches: &ArgMatches) -> Option { + if never_use_pin(yk, slot_id) { + None + } else { + pinutil::read_pin(sub_arg_matches) + } +} + +pub fn never_use_pin(yk: &mut YubiKey, slot_id: SlotId) -> bool { + match get(yk, slot_id) { + None => false, + Some((pin_policy, _)) => pin_policy == PinPolicy::Never, + } +} + +pub fn get(yk: &mut YubiKey, slot_id: SlotId) -> Option<(PinPolicy, TouchPolicy)> { + let slot_metadata = match metadata(yk, slot_id){ + Ok(meta) => meta, + Err(_) => return None, + }; + slot_metadata.policy +}