feat: updates

This commit is contained in:
2025-03-28 07:35:53 +08:00
parent 1be5754ed1
commit 3a40d7f0ad
43 changed files with 324 additions and 289 deletions

View File

@@ -1,8 +1,7 @@
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use crate::hmacutil;
use crate::{cmdutil, hmacutil};
pub struct CommandImpl;
@@ -17,12 +16,11 @@ impl Command for CommandImpl {
.arg(Arg::with_name("sha256").short("2").long("sha256").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"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let challenge_bytes = hmacutil::get_challenge_bytes(sub_arg_matches)?;
let hmac_result = hmacutil::compute_yubikey_hmac(&challenge_bytes)?;

View File

@@ -2,10 +2,9 @@ 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::EcdsaAlgorithm;
use crate::{argsutil, ecdsautil};
use crate::{argsutil, cmdutil, ecdsautil};
pub struct CommandImpl;
@@ -19,12 +18,11 @@ impl Command for CommandImpl {
.arg(Arg::with_name("file").short("f").long("file").takes_value(true).help("Input file"))
.arg(Arg::with_name("input").short("i").long("input").takes_value(true).help("Input"))
.arg(Arg::with_name("hash-hex").short("x").long("hash-hex").takes_value(true).help("Hash"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let hash_bytes = argsutil::get_sha256_digest_or_hash(sub_arg_matches)?;
let public_key = if let Some(public_key_hex) = sub_arg_matches.value_of("public-key-hex") {

View File

@@ -10,7 +10,7 @@ use x509_parser::nom::AsBytes;
use yubikey::{Key, YubiKey};
use yubikey::piv::{sign_data, SlotId};
use crate::{argsutil, pinutil, pivutil};
use crate::{argsutil, cmdutil, pinutil, pivutil};
use crate::digest::sha256_bytes;
use crate::signfile::{CERTIFICATES_SEARCH_URL, HASH_ALGORITHM_SHA256, SIGNATURE_ALGORITHM_SHA256_WITH_ECDSA, SignFileRequest, SIMPLE_SIG_SCHEMA, SimpleSignFile, SimpleSignFileSignature};
use crate::util::base64_encode;
@@ -40,10 +40,9 @@ 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(cmdutil::build_slot_arg())
.arg(cmdutil::build_pin_arg())
.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"))
.arg(Arg::with_name("filename").short("n").long("filename").takes_value(true).help("Filename"))
.arg(Arg::with_name("sign-file").short("S").long("sign-file").takes_value(false).help("Sign file"))

View File

@@ -1,9 +1,8 @@
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use std::collections::BTreeMap;
use crate::hmacutil;
use crate::{cmdutil, hmacutil};
pub struct CommandImpl;
@@ -21,14 +20,11 @@ impl Command for CommandImpl {
.takes_value(true)
.help("Ciphertext"),
)
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let ciphertext = sub_arg_matches.value_of("ciphertext").unwrap();
let plaintext = hmacutil::hmac_decrypt_to_string(ciphertext)?;

View File

@@ -1,9 +1,8 @@
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use std::collections::BTreeMap;
use crate::hmacutil;
use crate::{cmdutil, hmacutil};
pub struct CommandImpl;
@@ -21,14 +20,11 @@ impl Command for CommandImpl {
.takes_value(true)
.help("Plaintext"),
)
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let plaintext = sub_arg_matches.value_of("plaintext").unwrap();
let hmac_encrypt_ciphertext = hmacutil::hmac_encrypt_from_string(plaintext)?;

View File

@@ -1,8 +1,8 @@
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::{util_msg, XResult};
use rust_util::XResult;
use rust_util::util_clap::{Command, CommandError};
use crate::hmacutil;
use crate::{cmdutil, hmacutil};
pub struct CommandImpl;
@@ -19,12 +19,11 @@ impl Command for CommandImpl {
.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("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let variable = sub_arg_matches.is_present("variable");
let secret_bytes = get_secret_bytes(sub_arg_matches)?;

View File

@@ -1,8 +1,7 @@
use crate::keychain::{KeychainKey, KeychainKeyValue};
use crate::{ecdsautil, hmacutil};
use crate::{cmdutil, ecdsautil, hmacutil};
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use std::collections::BTreeMap;
pub struct CommandImpl;
@@ -27,16 +26,13 @@ impl Command for CommandImpl {
.long("with-hmac-encrypt")
.help("With HMAC encrypt"),
)
.arg(
Arg::with_name("keychain-name")
.long("keychain-name")
.takes_value(true)
.help("Key chain name"),
)
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_keychain_name_arg())
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let json_output = cmdutil::check_json_output(sub_arg_matches);
let with_hmac_encrypt = sub_arg_matches.is_present("with-hmac-encrypt");
let key_type = sub_arg_matches.value_of("type").unwrap().to_lowercase();
let keychain_name = sub_arg_matches.value_of("keychain-name");
@@ -48,11 +44,6 @@ impl Command for CommandImpl {
}
}
let json_output = sub_arg_matches.is_present("json");
if json_output {
util_msg::set_logger_std_out(false);
}
let (pkcs8_base64, secret_key_pem, public_key_pem, jwk_ec_key) = match key_type.as_str() {
"p256" => ecdsautil::generate_p256_keypair()?,
"p384" => ecdsautil::generate_p384_keypair()?,

View File

@@ -1,5 +1,6 @@
use crate::cmdutil;
use crate::keychain::{KeychainKey, KeychainKeyValue};
use clap::{App, Arg, ArgMatches, SubCommand};
use clap::{App, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
@@ -13,12 +14,7 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name())
.about("Export software keypair from keychain")
.arg(
Arg::with_name("keychain-name")
.long("keychain-name")
.takes_value(true)
.help("Key chain name"),
)
.arg(cmdutil::build_keychain_name_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {

View File

@@ -1,6 +1,7 @@
use crate::keychain::KeychainKey;
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use crate::cmdutil;
pub struct CommandImpl;
@@ -12,12 +13,7 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name())
.about("Import software keypair to keychain")
.arg(
Arg::with_name("keychain-name")
.long("keychain-name")
.takes_value(true)
.help("Key chain name"),
)
.arg(cmdutil::build_keychain_name_arg())
.arg(
Arg::with_name("import-key-value")
.long("import-key-value")

View File

@@ -1,9 +1,9 @@
use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use clap::{App, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use yubikey::YubiKey;
use crate::cmdutil;
pub struct CommandImpl;
@@ -12,12 +12,11 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("YubiKey list")
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");

View File

@@ -3,8 +3,7 @@ use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use crate::cmdutil;
use crate::ecdsautil::parse_ecdsa_r_and_s;
use crate::util::try_decode;
@@ -25,14 +24,11 @@ impl Command for CommandImpl {
.takes_value(true)
.help("ECDSA signature"),
)
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let mut json = BTreeMap::<&'_ str, String>::new();

View File

@@ -21,7 +21,7 @@ impl Command for CommandImpl {
.arg(Arg::with_name("in").short("i").long("in").takes_value(true).help("File input, *.pgp or *.asc"))
.arg(Arg::with_name("detail").long("detail").help("Detail output"))
.arg(Arg::with_name("verbose").long("verbose").help("Verbose output"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
// .arg(Arg::with_name("json").long("json").help("JSON output"))
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {

View File

@@ -1,12 +1,12 @@
use bech32::{ToBase32, Variant};
use clap::{App, Arg, ArgMatches, SubCommand};
use clap::{App, ArgMatches, SubCommand};
use openpgp_card::algorithm::{Algo, Curve};
use openpgp_card::crypto_data::{EccType, PublicKeyMaterial};
use openpgp_card::{KeyType, OpenPgp};
use openpgp_card_pcsc::PcscBackend;
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use std::collections::BTreeMap;
use crate::cmdutil;
const AGE_PUBLIC_KEY_PREFIX: &str = "age";
@@ -17,12 +17,11 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card encryption key to age address")
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let cards = opt_result!(PcscBackend::cards(None), "Failed to list OpenPGP cards: {}");

View File

@@ -2,10 +2,10 @@ use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use openpgp_card::crypto_data::Cryptogram;
use rust_util::{util_msg, XResult};
use rust_util::XResult;
use rust_util::util_clap::{Command, CommandError};
use crate::{pgpcardutil, pinutil};
use crate::{cmdutil, pgpcardutil, pinutil};
use crate::util::{base64_encode, read_stdin, try_decode};
#[derive(Debug, Clone, Copy)]
@@ -36,12 +36,11 @@ impl Command for CommandImpl {
.arg(Arg::with_name("ciphertext").short("c").long("ciphertext").takes_value(true).help("Cipher text (HEX or Base64)"))
.arg(Arg::with_name("stdin").long("stdin").help("Standard input (Ciphertext)"))
.arg(Arg::with_name("algo").long("algo").takes_value(true).help("Algo: RSA, X25519/ECDH"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let pin_opt = sub_arg_matches.value_of("pass").or_else(|| sub_arg_matches.value_of("pin"));
let pin_opt = pinutil::get_pin(pin_opt);

View File

@@ -4,8 +4,7 @@ use clap::{App, Arg, ArgMatches, SubCommand};
use openpgp_card::{KeyType, OpenPgp};
use openpgp_card_pcsc::PcscBackend;
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use crate::cmdutil;
use crate::pkiutil::openpgp_card_public_key_pem as public_key_pem;
pub struct CommandImpl;
@@ -16,13 +15,13 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card list subcommand")
.arg(Arg::with_name("detail").long("detail").help("Detail output"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let json_output = cmdutil::check_json_output(sub_arg_matches);
let detail_output = sub_arg_matches.is_present("detail");
let json_output = sub_arg_matches.is_present("json");
if json_output { util_msg::set_logger_std_out(false); }
let mut jsons = vec![];
let cards = opt_result!(PcscBackend::cards(None), "Failed to list OpenPGP cards: {}");

View File

@@ -5,11 +5,11 @@ use std::io::{ErrorKind, Read};
use clap::{App, Arg, ArgMatches, SubCommand};
use digest::Digest;
use openpgp_card::crypto_data::Hash;
use rust_util::{util_msg, XResult};
use rust_util::XResult;
use rust_util::util_clap::{Command, CommandError};
use sha2::{Sha256, Sha384, Sha512};
use crate::{pgpcardutil, pinutil};
use crate::{cmdutil, pgpcardutil, pinutil};
use crate::util::base64_encode;
const BUFF_SIZE: usize = 512 * 1024;
@@ -49,12 +49,11 @@ impl Command for CommandImpl {
.arg(Arg::with_name("use-sha384").long("use-sha384").help("Use SHA384 for file in"))
.arg(Arg::with_name("use-sha512").long("use-sha512").help("Use SHA512 for file in"))
.arg(Arg::with_name("algo").long("algo").takes_value(true).help("Algorithm, rsa, ecdsa, eddsa"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let pin_opt = sub_arg_matches.value_of("pass").or_else(|| sub_arg_matches.value_of("pin"));
let pin_opt = pinutil::get_pin(pin_opt);

View File

@@ -2,11 +2,10 @@ use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use yubikey::piv::AlgorithmId;
use yubikey::YubiKey;
use crate::{pinutil, pivutil};
use crate::{cmdutil, pinutil, pivutil};
use crate::util::{read_stdin, try_decode};
pub struct CommandImpl;
@@ -16,17 +15,16 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
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(cmdutil::build_slot_arg())
.arg(cmdutil::build_pin_arg())
.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"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(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");

View File

@@ -4,11 +4,10 @@ use std::fs;
use clap::{App, Arg, ArgMatches, SubCommand};
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::{ecdhutil, pinutil, pivutil};
use crate::{cmdutil, ecdhutil, pinutil, pivutil};
use crate::pivutil::get_algorithm_id;
pub struct CommandImpl;
@@ -18,9 +17,9 @@ 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(cmdutil::build_pin_arg())
.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(cmdutil::build_slot_arg())
.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)"))
.arg(Arg::with_name("private").long("private").help("Private key(PIV)"))
@@ -28,12 +27,11 @@ impl Command for CommandImpl {
.arg(Arg::with_name("public-key").long("public-key").takes_value(true).help("Public key"))
.arg(Arg::with_name("public-key-file").long("public-key-file").takes_value(true).help("Public key"))
.arg(Arg::with_name("public-key-point-hex").long("public-key-point-hex").takes_value(true).help("Public key point hex"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let public256 = sub_arg_matches.is_present("public-256");
let public384 = sub_arg_matches.is_present("public-384");

View File

@@ -2,13 +2,12 @@ use std::collections::BTreeMap;
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::{metadata, sign_data, AlgorithmId, ManagementAlgorithmId};
use yubikey::YubiKey;
use crate::util::base64_encode;
use crate::{argsutil, pivutil};
use crate::{argsutil, cmdutil, pivutil};
pub struct CommandImpl;
@@ -17,19 +16,18 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV EC sign(with SHA256) subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN"))
.arg(cmdutil::build_slot_arg())
.arg(cmdutil::build_pin_arg())
.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("algorithm").short("a").long("algorithm").takes_value(true).help("Algorithm, p256 or p384"))
.arg(Arg::with_name("file").short("f").long("file").takes_value(true).help("Input file"))
.arg(Arg::with_name("input").short("i").long("input").takes_value(true).help("Input"))
.arg(Arg::with_name("hash-hex").short("x").long("hash-hex").takes_value(true).help("Hash"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let mut json = BTreeMap::<&'_ str, String>::new();

View File

@@ -1,10 +1,9 @@
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use yubikey::{PinPolicy, piv, TouchPolicy, YubiKey};
use yubikey::piv::{AlgorithmId, SlotId};
use crate::pinutil;
use crate::{cmdutil, pinutil};
pub struct CommandImpl;
@@ -13,15 +12,12 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV generate subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN"))
.arg(cmdutil::build_pin_arg())
.arg(Arg::with_name("force").long("force").help("Force generate"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
// .arg(cmdutil::build_json_arg())
}
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); }
warning!("This feature is not works");
let pin_opt = sub_arg_matches.value_of("pin");
let pin_opt = pinutil::get_pin(pin_opt);

View File

@@ -1,6 +1,6 @@
use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use clap::{App, ArgMatches, SubCommand};
use p256::pkcs8::der::Encode;
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
@@ -9,7 +9,7 @@ use x509_parser::parse_x509_certificate;
use yubikey::{Key, YubiKey};
use yubikey::piv::{AlgorithmId, metadata};
use crate::pivutil;
use crate::{cmdutil, pivutil};
use crate::pivutil::{get_algorithm_id_by_certificate, slot_equals, ToStr};
use crate::pkiutil::bytes_to_pem;
use crate::sshutil::SshVecWriter;
@@ -22,13 +22,12 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV meta 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("json").long("json").help("JSON output"))
.arg(cmdutil::build_slot_arg())
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let mut json = BTreeMap::<&'_ str, String>::new();

View File

@@ -2,11 +2,10 @@ use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use yubikey::{piv, YubiKey};
use yubikey::piv::{AlgorithmId, SlotId};
use crate::{pinutil, pivutil, rsautil};
use crate::{cmdutil, pinutil, pivutil, rsautil};
use crate::util::base64_encode;
pub struct CommandImpl;
@@ -16,16 +15,15 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
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(cmdutil::build_slot_arg())
.arg(cmdutil::build_pin_arg())
.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"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let pin_opt = pinutil::read_pin(sub_arg_matches);

View File

@@ -1,6 +1,6 @@
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::{util_msg, XResult};
use rust_util::XResult;
use serde::Serialize;
use serde_json::{Map, Value};
use spki::der::Encode;
@@ -9,7 +9,7 @@ use tabled::{Table, Tabled};
use x509_parser::parse_x509_certificate;
use yubikey::piv::{metadata, SlotId};
use yubikey::{Certificate, YubiKey};
use crate::cmdutil;
use crate::pivutil::{get_algorithm_id_by_certificate, ToStr, ORDERED_SLOTS};
const NA: &str = "N/A";
@@ -35,17 +35,17 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV subcommand")
.arg(Arg::with_name("table").long("table").help("Show table"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(Arg::with_name("all").long("all").help("Show all"))
.arg(Arg::with_name("ordered").long("ordered").help("Show ordered"))
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let json_output = cmdutil::check_json_output(sub_arg_matches);
let show_table = sub_arg_matches.is_present("table");
let show_all = sub_arg_matches.is_present("all");
let show_ordered = sub_arg_matches.is_present("ordered");
let json_output = sub_arg_matches.is_present("json");
if json_output { util_msg::set_logger_std_out(false); }
let mut output = Map::new();
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");

View File

@@ -2,12 +2,12 @@ use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use openssl::rsa::{Padding, Rsa};
use rust_util::{util_msg, XResult};
use rust_util::XResult;
use rust_util::util_clap::{Command, CommandError};
use yubikey::{Key, YubiKey};
use yubikey::piv::{AlgorithmId, SlotId};
use crate::{argsutil, ecdsautil, pivutil};
use crate::{argsutil, cmdutil, ecdsautil, pivutil};
use crate::ecdsautil::EcdsaAlgorithm;
use crate::pivutil::slot_equals;
@@ -18,17 +18,16 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV verify 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(cmdutil::build_slot_arg())
.arg(Arg::with_name("signature-hex").short("t").long("signature-hex").takes_value(true).help("Signature"))
.arg(Arg::with_name("file").short("f").long("file").takes_value(true).help("Input file"))
.arg(Arg::with_name("input").short("i").long("input").takes_value(true).help("Input"))
.arg(Arg::with_name("hash-hex").short("x").long("hash-hex").takes_value(true).help("Hash"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let hash_bytes = argsutil::get_sha256_digest_or_hash(sub_arg_matches)?;
let signature = if let Some(signature_hex) = sub_arg_matches.value_of("signature-hex") {

View File

@@ -8,7 +8,7 @@ use openssl::rsa::Rsa;
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use rust_util::util_msg::MessageType;
use crate::cmdutil;
use crate::util::{read_stdin, try_decode};
pub struct CommandImpl;
@@ -25,12 +25,11 @@ impl Command for CommandImpl {
.arg(Arg::with_name("stdin").long("stdin").help("Standard input (Ciphertext)"))
.arg(Arg::with_name("padding").long("padding").takes_value(true)
.possible_values(&["pkcs1", "oaep", "pss", "none"]).help("Padding"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let pri_key_in = opt_value_result!(sub_arg_matches.value_of("pri-key-in"), "Require private key in");
let pri_key_bytes = opt_result!(std::fs::read(pri_key_in), "Read file: {}, failed: {}", pri_key_in);

View File

@@ -6,8 +6,7 @@ use openssl::encrypt::Encrypter;
use openssl::pkey::PKey;
use openssl::rsa::Rsa;
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use crate::cmdutil;
use crate::digest::sha256_bytes;
pub struct CommandImpl;
@@ -23,12 +22,11 @@ impl Command for CommandImpl {
.arg(Arg::with_name("data-hex").long("data-hex").takes_value(true).help("Data in HEX"))
.arg(Arg::with_name("padding").long("padding").takes_value(true)
.possible_values(&["pkcs1", "oaep", "pss", "none"]).help("Padding"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let pub_key_in = opt_value_result!(sub_arg_matches.value_of("pub-key-in"), "Require public key in");
let pub_key_bytes = opt_result!(fs::read(pub_key_in), "Read file: {}, failed: {}", pub_key_in);

View File

@@ -1,4 +1,5 @@
use crate::seutil;
use std::collections::BTreeMap;
use crate::{cmdutil, seutil};
use clap::{App, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
@@ -11,15 +12,25 @@ impl Command for CommandImpl {
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("Secure Enclave subcommand")
// .arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, _sub_arg_matches: &ArgMatches) -> CommandError {
if seutil::is_support_se() {
success!("Secure Enclave is supported.")
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let json_output = cmdutil::check_json_output(sub_arg_matches);
let mut json = BTreeMap::new();
if json_output {
json.insert("se_supported", seutil::is_support_se());
} else {
failure!("Secure Enclave is NOT supported.")
if seutil::is_support_se() {
success!("Secure Enclave is supported.")
} else {
failure!("Secure Enclave is NOT supported.")
}
}
if json_output {}
Ok(None)
}
}

View File

@@ -1,10 +1,9 @@
use crate::keyutil::{parse_key_uri, KeyUri};
use crate::seutil;
use crate::{cmdutil, seutil};
use clap::{App, Arg, ArgMatches, SubCommand};
use p256::elliptic_curve::sec1::FromEncodedPoint;
use p256::{EncodedPoint, PublicKey};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use spki::EncodePublicKey;
use std::collections::BTreeMap;
@@ -32,21 +31,18 @@ impl Command for CommandImpl {
.takes_value(true)
.help("E-Public key"),
)
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let json_output = cmdutil::check_json_output(sub_arg_matches);
if !seutil::is_support_se() {
return simple_error!("Secure Enclave is NOT supported.");
}
let key = sub_arg_matches.value_of("key").unwrap();
let epk = sub_arg_matches.value_of("epk").unwrap();
let json_output = sub_arg_matches.is_present("json");
if json_output {
util_msg::set_logger_std_out(false);
}
let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?;
debugging!("Secure enclave key URI: {:?}", se_key_uri);

View File

@@ -1,9 +1,8 @@
use crate::keyutil::{parse_key_uri, KeyUri};
use crate::seutil;
use crate::{cmdutil, seutil};
use crate::util::{base64_decode, base64_encode};
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use std::collections::BTreeMap;
pub struct CommandImpl;
@@ -36,10 +35,12 @@ impl Command for CommandImpl {
.takes_value(true)
.help("Input in base64"),
)
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let json_output = cmdutil::check_json_output(sub_arg_matches);
if !seutil::is_support_se() {
return simple_error!("Secure Enclave is NOT supported.");
}
@@ -51,10 +52,6 @@ impl Command for CommandImpl {
},
Some(input) => input.as_bytes().to_vec(),
};
let json_output = sub_arg_matches.is_present("json");
if json_output {
util_msg::set_logger_std_out(false);
}
let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key)?;
debugging!("Secure enclave key URI: {:?}", se_key_uri);

View File

@@ -1,10 +1,9 @@
use crate::pkiutil::bytes_to_pem;
use crate::seutil;
use crate::{cmdutil, seutil};
use crate::util::base64_encode;
use clap::{App, Arg, ArgMatches, SubCommand};
use p256::PublicKey;
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use spki::DecodePublicKey;
use std::collections::BTreeMap;
@@ -37,19 +36,17 @@ impl Command for CommandImpl {
.long("disable-bio")
.help("Disable bio"),
)
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let json_output = cmdutil::check_json_output(sub_arg_matches);
if !seutil::is_support_se() {
return simple_error!("Secure Enclave is NOT supported.");
}
let ty = sub_arg_matches.value_of("type").unwrap();
let host = sub_arg_matches.value_of("host").unwrap_or("macbook");
let json_output = sub_arg_matches.is_present("json");
if json_output {
util_msg::set_logger_std_out(false);
}
let sign = match ty {
"signing" | "ecsign" | "sign" => true,

View File

@@ -1,9 +1,8 @@
use crate::cmd_se_generate::print_se_key;
use crate::keyutil::{parse_key_uri, KeyUri, KeyUsage};
use crate::seutil;
use crate::{cmdutil, seutil};
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
pub struct CommandImpl;
@@ -22,20 +21,17 @@ impl Command for CommandImpl {
.takes_value(true)
.help("Key uri"),
)
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let json_output = cmdutil::check_json_output(sub_arg_matches);
if !seutil::is_support_se() {
return simple_error!("Secure Enclave is NOT supported.");
}
let key_uri = sub_arg_matches.value_of("key").unwrap();
let json_output = sub_arg_matches.is_present("json");
if json_output {
util_msg::set_logger_std_out(false);
}
let KeyUri::SecureEnclaveKey(se_key_uri) = parse_key_uri(key_uri)?;
debugging!("Secure enclave key URI: {:?}", se_key_uri);

View File

@@ -2,45 +2,44 @@ use std::borrow::Cow;
use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use jwt::{AlgorithmType, Header, ToBase64};
use jwt::header::HeaderType;
use rust_util::{util_msg, util_time, XResult};
use jwt::{AlgorithmType, Header, ToBase64};
use rust_util::util_clap::{Command, CommandError};
use rust_util::{util_time, XResult};
use serde_json::{Map, Number, Value};
use yubikey::piv::{sign_data, AlgorithmId, SlotId};
use yubikey::{Certificate, YubiKey};
use yubikey::piv::{AlgorithmId, sign_data, SlotId};
use crate::{digest, pivutil, rsautil, util};
use crate::ecdsautil::parse_ecdsa_to_rs;
use crate::{cmdutil, digest, pivutil, rsautil, util};
const SEPARATOR: &str = ".";
pub struct CommandImpl;
impl Command for CommandImpl {
fn name(&self) -> &str { "sign-jwt" }
fn name(&self) -> &str {
"sign-jwt"
}
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"))
let app = SubCommand::with_name(self.name()).about("Sign JWT subcommand")
.arg(cmdutil::build_pin_arg())
.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"))
.arg(Arg::with_name("payload").short("P").long("payload").takes_value(true).help("Claims in JSON"))
.arg(Arg::with_name("jti").long("jti").help("Claims jti"))
.arg(Arg::with_name("validity").long("validity").takes_value(true).help("Claims validity period e.g. 10m means 10 minutes (s - second, m - minute, h - hour, d - day)"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_slot_arg())
.arg(cmdutil::build_json_arg());
fill_sign_jwt_app_args(app)
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let mut json = BTreeMap::<&'_ str, String>::new();
let slot = opt_value_result!(
sub_arg_matches.value_of("slot"), "--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e");
sub_arg_matches.value_of("slot"),
"--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"
);
let (header, payload, jwt_claims) = build_jwt_parts(sub_arg_matches)?;
@@ -64,15 +63,34 @@ impl Command for CommandImpl {
}
}
fn sign_jwt(yk: &mut YubiKey, slot_id: SlotId, pin_opt: &Option<String>, mut header: Header, payload: &Option<String>, claims: &Map<String, Value>) -> XResult<String> {
pub fn fill_sign_jwt_app_args<'a>(app: App<'a, 'a>) -> App<'a, 'a> {
app.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"))
.arg(Arg::with_name("payload").short("P").long("payload").takes_value(true).help("Claims in JSON"))
.arg(Arg::with_name("jti").long("jti").help("Claims jti"))
.arg(Arg::with_name("validity").long("validity").takes_value(true).help("Claims validity period e.g. 10m means 10 minutes (s - second, m - minute, h - hour, d - day)"))
}
fn sign_jwt(
yk: &mut YubiKey,
slot_id: SlotId,
pin_opt: &Option<String>,
mut header: Header,
payload: &Option<String>,
claims: &Map<String, Value>,
) -> XResult<String> {
if let Some(pin) = pin_opt {
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");
opt_result!(
yk.verify_pin(pin.as_bytes()),
"YubiKey verify pin failed: {}"
);
}
let cert = match Certificate::read(yk, slot_id) {
Ok(c) => c,
Err(e) => return simple_error!("Read YubiKey certificate failed: {}", e),
};
let piv_algorithm_id = pivutil::get_algorithm_id(&cert.cert.tbs_certificate.subject_public_key_info)?;
let piv_algorithm_id =
pivutil::get_algorithm_id(&cert.cert.tbs_certificate.subject_public_key_info)?;
let (jwt_algorithm, yk_algorithm) = match piv_algorithm_id {
AlgorithmId::Rsa1024 => return simple_error!("RSA 1024 bits not supported."),
@@ -90,18 +108,21 @@ fn sign_jwt(yk: &mut YubiKey, slot_id: SlotId, pin_opt: &Option<String>, mut hea
let tobe_signed = merge_header_claims(header.as_bytes(), claims.as_bytes());
let raw_in = match jwt_algorithm {
AlgorithmType::Rs256 => rsautil::pkcs15_sha256_rsa_2048_padding_for_sign(
&digest::sha256_bytes(&tobe_signed)),
AlgorithmType::Rs256 => {
rsautil::pkcs15_sha256_rsa_2048_padding_for_sign(&digest::sha256_bytes(&tobe_signed))
}
AlgorithmType::Es256 => digest::sha256_bytes(&tobe_signed),
AlgorithmType::Es384 => digest::sha384_bytes(&tobe_signed),
_ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm),
};
let signed_data = opt_result!(
sign_data(yk, &raw_in, yk_algorithm, slot_id), "Sign YubiKey failed: {}");
sign_data(yk, &raw_in, yk_algorithm, slot_id),
"Sign YubiKey failed: {}"
);
let signed_data = match jwt_algorithm {
AlgorithmType::Rs256 => signed_data.to_vec(),
AlgorithmType::Es256 | AlgorithmType::Es384 => parse_ecdsa_to_rs(signed_data.as_slice())?,
AlgorithmType::Es256 | AlgorithmType::Es384 => parse_ecdsa_to_rs(signed_data.as_slice())?,
_ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm),
};
@@ -118,14 +139,21 @@ pub fn merge_header_claims(header: &[u8], claims: &[u8]) -> Vec<u8> {
tobe_signed
}
pub fn merge_payload_claims<'a>(payload: &'a Option<String>, claims: &'a Map<String, Value>) -> XResult<Cow<'a, str>> {
pub fn merge_payload_claims<'a>(
payload: &'a Option<String>,
claims: &'a Map<String, Value>,
) -> XResult<Cow<'a, str>> {
Ok(match (payload, claims.is_empty()) {
(Some(payload), true) => Cow::Owned(util::base64_encode_url_safe_no_pad(payload.as_bytes())),
(Some(payload), true) => {
Cow::Owned(util::base64_encode_url_safe_no_pad(payload.as_bytes()))
}
(_, _) => opt_result!(claims.to_base64(), "Claims to base64 failed: {}"),
})
}
pub fn build_jwt_parts(sub_arg_matches: &ArgMatches) -> XResult<(Header, Option<String>, Map<String, Value>)> {
pub fn build_jwt_parts(
sub_arg_matches: &ArgMatches,
) -> XResult<(Header, Option<String>, Map<String, Value>)> {
let key_id = sub_arg_matches.value_of("key-id");
let claims = sub_arg_matches.values_of("claims");
let payload = sub_arg_matches.value_of("payload");
@@ -145,8 +173,12 @@ pub fn build_jwt_parts(sub_arg_matches: &ArgMatches) -> XResult<(Header, Option<
jwt_claims.insert(k, v);
});
}
Ok(value) => { warning!("Not valid payload map: {}", value); }
Err(e) => { warning!("Not valid payload value: {}", e); }
Ok(value) => {
warning!("Not valid payload map: {}", value);
}
Err(e) => {
warning!("Not valid payload value: {}", e);
}
};
}
@@ -155,8 +187,12 @@ pub fn build_jwt_parts(sub_arg_matches: &ArgMatches) -> XResult<(Header, Option<
(_, Some(claims)) => {
for claim in claims {
match split_claim(claim) {
None => { warning!("Claim '{}' do not contains ':'", claim); }
Some((k, v)) => { jwt_claims.insert(k, v); }
None => {
warning!("Claim '{}' do not contains ':'", claim);
}
Some((k, v)) => {
jwt_claims.insert(k, v);
}
}
}
if !jwt_claims.contains_key("sub") {
@@ -168,15 +204,23 @@ pub fn build_jwt_parts(sub_arg_matches: &ArgMatches) -> XResult<(Header, Option<
// set jti, iat and sub
if jti && !jwt_claims.contains_key("jti") {
jwt_claims.insert("jti".to_string(), Value::String(format!("jti-{}", util_time::get_current_millis())));
jwt_claims.insert(
"jti".to_string(),
Value::String(format!("jti-{}", util_time::get_current_millis())),
);
}
if let Some(validity) = validity {
match util_time::parse_duration(validity) {
None => { warning!("Bad validity: {}", validity) }
None => {
warning!("Bad validity: {}", validity)
}
Some(validity) => {
let current_secs = (util_time::get_current_millis() / 1000) as u64;
jwt_claims.insert("iat".to_string(), Value::Number(Number::from(current_secs)));
jwt_claims.insert("exp".to_string(), Value::Number(Number::from(current_secs + validity.as_secs())));
jwt_claims.insert(
"exp".to_string(),
Value::Number(Number::from(current_secs + validity.as_secs())),
);
}
}
}

View File

@@ -4,14 +4,14 @@ use clap::{App, Arg, ArgMatches, SubCommand};
use jwt::{AlgorithmType, Header, ToBase64};
use rust_util::util_clap::{Command, CommandError};
use rust_util::{util_msg, XResult};
use rust_util::XResult;
use serde_json::{Map, Value};
use std::collections::BTreeMap;
use crate::cmd_sign_jwt::{build_jwt_parts, merge_header_claims, merge_payload_claims};
use crate::ecdsautil::parse_ecdsa_to_rs;
use crate::keyutil::{parse_key_uri, KeyUri};
use crate::{hmacutil, util};
use crate::{cmd_sign_jwt, cmdutil, hmacutil, util};
const SEPARATOR: &str = ".";
@@ -23,21 +23,14 @@ impl Command for CommandImpl {
}
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("Sign JWT subcommand")
let app = SubCommand::with_name(self.name()).about("Sign JWT subcommand")
.arg(Arg::with_name("key").long("key").required(true).takes_value(true).help("Key uri"))
.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"))
.arg(Arg::with_name("payload").short("P").long("payload").takes_value(true).help("Claims in JSON"))
.arg(Arg::with_name("jti").long("jti").help("Claims jti"))
.arg(Arg::with_name("validity").long("validity").takes_value(true).help("Claims validity period e.g. 10m means 10 minutes (s - second, m - minute, h - hour, d - day)"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg());
cmd_sign_jwt::fill_sign_jwt_app_args(app)
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let mut json = BTreeMap::<&'_ str, String>::new();

View File

@@ -3,12 +3,12 @@ use std::collections::BTreeMap;
use clap::{App, Arg, ArgMatches, SubCommand};
use jwt::{AlgorithmType, Header, ToBase64};
use rust_util::util_clap::{Command, CommandError};
use rust_util::{util_msg, XResult};
use rust_util::XResult;
use serde_json::{Map, Value};
use crate::cmd_sign_jwt::{build_jwt_parts, merge_header_claims, merge_payload_claims};
use crate::keychain::{KeychainKey, KeychainKeyValue};
use crate::{digest, ecdsautil, hmacutil, keychain, rsautil, util};
use crate::{cmd_sign_jwt, cmdutil, digest, ecdsautil, hmacutil, keychain, rsautil, util};
const SEPARATOR: &str = ".";
@@ -20,21 +20,14 @@ impl Command for CommandImpl {
}
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("Sign JWT subcommand")
let app = SubCommand::with_name(self.name()).about("Sign JWT subcommand")
.arg(Arg::with_name("private-key").short("k").long("private-key").takes_value(true).help("Private key PKCS#8"))
.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"))
.arg(Arg::with_name("payload").short("P").long("payload").takes_value(true).help("Claims in JSON"))
.arg(Arg::with_name("jti").long("jti").help("Claims jti"))
.arg(Arg::with_name("validity").long("validity").takes_value(true).help("Claims validity period e.g. 10m means 10 minutes (s - second, m - minute, h - hour, d - day)"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg());
cmd_sign_jwt::fill_sign_jwt_app_args(app)
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let mut json = BTreeMap::<&'_ str, String>::new();

View File

@@ -15,7 +15,7 @@ use yubikey::{Key, YubiKey};
use crate::digest::{sha256_bytes, sha384_bytes};
use crate::pivutil::slot_equals;
use crate::{pinutil, pivutil, util};
use crate::{cmdutil, pinutil, pivutil, util};
pub struct CommandImpl;
@@ -27,9 +27,9 @@ 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(cmdutil::build_pin_arg())
.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(cmdutil::build_slot_arg())
.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"))
.arg(Arg::with_name("pub").short("f").long("pub").alias("pub-file").required(true).takes_value(true).help("SSH public key file"))

View File

@@ -6,7 +6,7 @@ use rust_util::util_msg;
use yubikey::{Key, YubiKey};
use yubikey::piv::{AlgorithmId, sign_data};
use crate::{pinutil, pivutil, util};
use crate::{cmdutil, pinutil, pivutil, util};
use crate::pivutil::{get_algorithm_id_by_certificate, slot_equals, ToStr};
use crate::sshutil::SshVecWriter;
@@ -18,9 +18,9 @@ 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(cmdutil::build_slot_arg())
.arg(cmdutil::build_pin_arg())
.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"))
}

View File

@@ -1,30 +1,39 @@
use crate::digest::sha256_bytes;
use crate::pivutil;
use crate::pivutil::{get_algorithm_id_by_certificate, slot_equals, ToStr};
use crate::sshutil::SshVecWriter;
use crate::{cmdutil, pivutil};
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
use clap::{App, Arg, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use std::collections::BTreeMap;
use yubikey::piv::AlgorithmId;
use yubikey::{Key, YubiKey};
pub struct CommandImpl;
impl Command for CommandImpl {
fn name(&self) -> &str { "ssh-pub-key" }
fn name(&self) -> &str {
"ssh-pub-key"
}
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("SSH public key 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"))
SubCommand::with_name(self.name())
.about("SSH public key subcommand")
.arg(cmdutil::build_slot_arg())
.arg(Arg::with_name("ca").long("ca").help("SSH cert-authority"))
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
util_msg::set_logger_std_out(false);
let json_output = cmdutil::check_json_output(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 mut json = BTreeMap::<&'_ str, String>::new();
let slot = opt_value_result!(
sub_arg_matches.value_of("slot"),
"--slot must assigned, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"
);
let ca = sub_arg_matches.is_present("ca");
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");
let slot_id = pivutil::get_slot_id(slot)?;
@@ -33,19 +42,28 @@ impl Command for CommandImpl {
let mut ec_key_point = vec![];
match Key::list(&mut yk) {
Err(e) => warning!("List keys failed: {}", e),
Ok(keys) => for k in &keys {
let slot_str = format!("{:x}", Into::<u8>::into(k.slot()));
if slot_equals(&slot_id, &slot_str) {
let cert = &k.certificate().cert.tbs_certificate;
let certificate = k.certificate();
if let Ok(algorithm_id) = get_algorithm_id_by_certificate(certificate) {
match algorithm_id {
AlgorithmId::EccP256 | AlgorithmId::EccP384 => {
let public_key_bit_string = &cert.subject_public_key_info.subject_public_key;
ec_key_point.extend_from_slice(public_key_bit_string.raw_bytes());
algorithm_id_opt = Some(algorithm_id);
Ok(keys) => {
for k in &keys {
let slot_str = format!("{:x}", Into::<u8>::into(k.slot()));
if slot_equals(&slot_id, &slot_str) {
let cert = &k.certificate().cert.tbs_certificate;
let certificate = k.certificate();
if let Ok(algorithm_id) = get_algorithm_id_by_certificate(certificate) {
match algorithm_id {
AlgorithmId::EccP256 | AlgorithmId::EccP384 => {
let public_key_bit_string =
&cert.subject_public_key_info.subject_public_key;
ec_key_point
.extend_from_slice(public_key_bit_string.raw_bytes());
algorithm_id_opt = Some(algorithm_id);
}
_ => {
return simple_error!(
"Not P256/384 key: {}",
algorithm_id.to_str()
)
}
}
_ => return simple_error!("Not P256/384 key: {}", algorithm_id.to_str()),
}
}
}
@@ -82,18 +100,34 @@ impl Command for CommandImpl {
ssh_pub_key.write_bytes(&ecc_key_blob);
let ssh_pub_key_sha256 = sha256_bytes(&ssh_pub_key);
information!("SSH key SHA256: {} (base64)", STANDARD.encode(&ssh_pub_key_sha256));
information!(
"SSH key SHA256: {} (base64)",
STANDARD.encode(&ssh_pub_key_sha256)
);
information!("SSH key SHA256: {} (hex)", hex::encode(&ssh_pub_key_sha256));
eprintln!();
println!(
let ssh_pub_key = format!(
"{}ecdsa-sha2-{} {} Yubikey-PIV-{}",
if ca { "cert-authority,principals=\"root\" " } else { "" },
if ca {
"cert-authority,principals=\"root\" "
} else {
""
},
ssh_algorithm,
STANDARD.encode(&ssh_pub_key),
slot_id
);
if json_output {
json.insert("ssh_pub_key", ssh_pub_key);
} else {
println!("{}", &ssh_pub_key);
}
if json_output {
println!("{}", serde_json::to_string_pretty(&json).unwrap());
}
Ok(None)
}
}

View File

@@ -9,11 +9,10 @@ use openssl::hash::MessageDigest;
use openssl::pkey::PKey;
use openssl::sign::Verifier;
use rust_util::util_clap::{Command, CommandError};
use rust_util::util_msg;
use x509_parser::certificate::X509Certificate;
use x509_parser::prelude::FromDer;
use crate::digest;
use crate::{cmdutil, digest};
use crate::fido;
use crate::fido::{U2fRegistrationData, U2fV2Challenge};
use crate::util::base64_encode;
@@ -29,12 +28,11 @@ impl Command for CommandImpl {
.arg(Arg::with_name("timeout").short("t").long("timeout").default_value("30").help("Timeout in seconds"))
.arg(Arg::with_name("challenge").long("challenge").takes_value(true).help("Challenge HEX"))
.arg(Arg::with_name("challenge-with-timestamp-prefix").long("challenge-with-timestamp-prefix").help("Challenge with timestamp prefix"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
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 json_output = cmdutil::check_json_output(sub_arg_matches);
let timeout_ms = match sub_arg_matches.value_of("timeout").unwrap().parse::<u32>() {
Ok(t) => (t * 1000) as u64,

View File

@@ -13,7 +13,7 @@ use openssl::pkey::PKey;
use openssl::sign::Verifier;
use rust_util::util_clap::{Command, CommandError};
use crate::digest;
use crate::{cmdutil, digest};
use crate::fido;
use crate::fido::U2fV2Challenge;
use crate::util::base64_encode;
@@ -31,12 +31,11 @@ impl Command for CommandImpl {
.arg(Arg::with_name("challenge").long("challenge").takes_value(true).help("Challenge HEX"))
.arg(Arg::with_name("challenge-with-timestamp-prefix").long("challenge-with-timestamp-prefix").help("Challenge with timestamp prefix"))
.arg(Arg::with_name("key-handle").short("k").long("key-handle").takes_value(true).multiple(true).help("Key handle"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
.arg(cmdutil::build_json_arg())
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
let json_output = sub_arg_matches.is_present("json");
if json_output { rust_util::util_msg::set_logger_std_out(false); }
let json_output = cmdutil::check_json_output(sub_arg_matches);
let timeout_ms = match sub_arg_matches.value_of("timeout").unwrap().parse::<u32>() {
Ok(t) => (t * 1000) as u64,

33
src/cmdutil.rs Normal file
View File

@@ -0,0 +1,33 @@
use clap::{Arg, ArgMatches};
use rust_util::util_msg;
pub fn build_slot_arg() -> Arg<'static, 'static> {
Arg::with_name("slot")
.short("s")
.long("slot")
.takes_value(true)
.help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e")
}
pub fn build_pin_arg() -> Arg<'static, 'static> {
Arg::with_name("pin").short("p").long("pin").takes_value(true).help("PIV card user PIN")
}
pub fn build_keychain_name_arg() -> Arg<'static, 'static> {
Arg::with_name("keychain-name")
.long("keychain-name")
.takes_value(true)
.help("Key chain name")
}
pub fn build_json_arg() -> Arg<'static, 'static> {
Arg::with_name("json").long("json").help("JSON output")
}
pub fn check_json_output(sub_arg_matches: &ArgMatches) -> bool {
let json_output = sub_arg_matches.is_present("json");
if json_output {
util_msg::set_logger_std_out(false);
}
json_output
}

View File

@@ -71,6 +71,7 @@ mod signfile;
mod sshutil;
mod util;
mod keychain;
mod cmdutil;
pub struct DefaultCommandImpl;