feat: v1.9.4

This commit is contained in:
2024-06-16 00:07:50 +08:00
parent 320664bfa0
commit 32ab2d3d6d
21 changed files with 60 additions and 39 deletions

2
Cargo.lock generated
View File

@@ -368,7 +368,7 @@ dependencies = [
[[package]]
name = "card-cli"
version = "1.9.3"
version = "1.9.4"
dependencies = [
"authenticator",
"base64 0.21.7",

View File

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

View File

@@ -32,11 +32,19 @@ hW53WfImja+b5kwwyqUikyMCAwEAAQ==
encrypt
```
$ openssl rsautl -encrypt -pubin -inkey enc_key.pem -in test.txt -out enc.txt -pkcs
OR
$ openssl pkeyutl -encrypt -inkey enc_key.pem -pubin -in a.txt -out enc.txt
```
decrypt
```
$ card-cli pgp-card-decrypt -c $(cat enc.txt | xxd -ps -c 11111)
OR
$ card-cli piv-decrypt -s r3 -c "$(cat enc.txt | base64)"
```
## sign & verify

View File

@@ -14,7 +14,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "pgp-age-address" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card Encryption key to age address")
SubCommand::with_name(self.name()).about("OpenPGP Card encryption key to age address")
}
fn run(&self, _arg_matches: &ArgMatches, _sub_arg_matches: &ArgMatches) -> CommandError {

View File

@@ -10,7 +10,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "pgp-card-admin" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card Admin subcommand")
SubCommand::with_name(self.name()).about("OpenPGP Card admin subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).default_value("12345678").help("OpenPGP card admin pin"))
.arg(Arg::with_name("pass").long("pass").takes_value(true).help("[deprecated] now OpenPGP card admin pin"))
.arg(Arg::with_name("name").short("n").long("name").takes_value(true).required(false).help("Set name"))

View File

@@ -6,7 +6,7 @@ use rust_util::{util_msg, XResult};
use rust_util::util_clap::{Command, CommandError};
use crate::pgpcardutil;
use crate::util::{base64_decode, base64_encode};
use crate::util::{base64_encode, try_decode};
#[derive(Debug, Clone, Copy)]
enum EncryptAlgo {
@@ -30,11 +30,10 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "pgp-card-decrypt" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card Decrypt subcommand")
SubCommand::with_name(self.name()).about("OpenPGP Card decrypt subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).default_value("123456").help("OpenPGP card user pin"))
.arg(Arg::with_name("pass").long("pass").takes_value(true).help("[deprecated] now OpenPGP card user pin"))
.arg(Arg::with_name("cipher").short("c").long("cipher").takes_value(true).help("Cipher text HEX"))
.arg(Arg::with_name("cipher-base64").short("b").long("cipher-base64").takes_value(true).help("Cipher text base64"))
.arg(Arg::with_name("ciphertext").short("c").long("ciphertext").takes_value(true).help("Cipher text (HEX or Base64)"))
.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"))
}
@@ -47,18 +46,15 @@ impl Command for CommandImpl {
let pin = opt_value_result!(pin_opt, "User pin must be assigned");
if pin.len() < 6 { return simple_error!("User pin length:{}, must >= 6!", pin.len()); }
let cipher = sub_arg_matches.value_of("cipher");
let cipher_base64 = sub_arg_matches.value_of("cipher-base64");
let ciphertext = sub_arg_matches.value_of("ciphertext");
let algo = sub_arg_matches.value_of("algo").unwrap_or("rsa").to_lowercase();
let algo = EncryptAlgo::from_str(&algo)?;
let cipher_bytes = if let Some(cipher) = cipher {
opt_result!(hex::decode(cipher), "Decode cipher failed: {}")
} else if let Some(cipher_base64) = cipher_base64 {
opt_result!(base64_decode(cipher_base64), "Decode cipher-base64 failed: {}")
let ciphertext_bytes = if let Some(ciphertext) = ciphertext {
opt_result!(try_decode(ciphertext), "Decode cipher failed: {}")
} else {
return simple_error!("cipher or cipher-base64 must assign one");
return simple_error!("--ciphertext must be assigned");
};
let mut pgp = pgpcardutil::get_openpgp_card()?;
@@ -68,8 +64,8 @@ impl Command for CommandImpl {
success!("User pin verify success!");
let text = match algo {
EncryptAlgo::Rsa => trans.decipher(Cryptogram::RSA(&cipher_bytes))?,
EncryptAlgo::Ecdh => trans.decipher(Cryptogram::ECDH(&cipher_bytes))?,
EncryptAlgo::Rsa => trans.decipher(Cryptogram::RSA(&ciphertext_bytes))?,
EncryptAlgo::Ecdh => trans.decipher(Cryptogram::ECDH(&ciphertext_bytes))?,
};
success!("Clear text HEX: {}", hex::encode(&text));
success!("Clear text base64: {}", base64_encode(&text));
@@ -80,8 +76,8 @@ impl Command for CommandImpl {
if json_output {
let mut json = BTreeMap::<&'_ str, String>::new();
json.insert("cipher_hex", hex::encode(&cipher_bytes));
json.insert("cipher_base64", base64_encode(&cipher_bytes));
json.insert("cipher_hex", hex::encode(&ciphertext_bytes));
json.insert("cipher_base64", base64_encode(&ciphertext_bytes));
json.insert("text_hex", hex::encode(&text));
json.insert("text_base64", base64_encode(&text));
if let Some(text) = text_opt {

View File

@@ -14,7 +14,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "pgp-card-list" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card List subcommand")
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"))
}

View File

@@ -127,7 +127,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "pgp-card-make" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card Make subcommand")
SubCommand::with_name(self.name()).about("OpenPGP Card make subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).default_value("12345678").help("OpenPGP card admin pin"))
.arg(Arg::with_name("pass").long("pass").takes_value(true).required(false).help("Password for PGP secret key"))
.arg(Arg::with_name("in").long("in").takes_value(true).required(false).help("PGP file in"))

View File

@@ -38,7 +38,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "pgp-card-sign" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("OpenPGP Card Sign subcommand")
SubCommand::with_name(self.name()).about("OpenPGP Card sign subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).default_value("123456").help("OpenPGP card user pin"))
.arg(Arg::with_name("pass").long("pass").takes_value(true).help("[deprecated] now OpenPGP card user pin"))
.arg(Arg::with_name("sha256").short("2").long("sha256").takes_value(true).help("Digest SHA256 HEX"))

View File

@@ -7,6 +7,7 @@ use yubikey::piv::AlgorithmId;
use yubikey::YubiKey;
use crate::pivutil;
use crate::util::try_decode;
pub struct CommandImpl;
@@ -14,10 +15,10 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "piv-decrypt" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV Decrypt(RSA) subcommand")
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).default_value("123456").help("OpenPGP card user pin"))
.arg(Arg::with_name("encrypted-data").long("encrypted-data").takes_value(true).help("Encrypted data (HEX)"))
.arg(Arg::with_name("ciphertext").long("ciphertext").short("c").takes_value(true).help("Encrypted data (HEX or Base64)"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
}
@@ -30,18 +31,18 @@ impl Command for CommandImpl {
let pin_opt = sub_arg_matches.value_of("pin");
let pin = opt_value_result!(pin_opt, "User pin must be assigned");
let encrypted_data = if let Some(encrypted_data_hex) = sub_arg_matches.value_of("encrypted-data") {
opt_result!(hex::decode(encrypted_data_hex), "Decode --encrypted-data failed: {}")
let encrypted_data = if let Some(ciphertext) = sub_arg_matches.value_of("ciphertext") {
opt_result!(try_decode(ciphertext), "Decode --ciphertext failed: {}")
} else {
return simple_error!("Argument --data or --data-hex must assign one");
return simple_error!("Argument --ciphertext must be assigned");
};
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");
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, AlgorithmId::Rsa2048, slot_id);
// let sign_result = yubikey::piv::sign_data(&mut yk, &encrypted_data, AlgorithmId::Rsa2048, SlotId::KeyManagement);
let decrypt_result = yubikey::piv::decrypt_data(&mut yk, &encrypted_data,
AlgorithmId::Rsa2048, slot_id);
let decrypted_data = opt_result!(decrypt_result, "Decrypt data failed: {}");
let decrypted_data_bytes = decrypted_data.as_slice();

View File

@@ -16,7 +16,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "piv-ecsign" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV EC Sign(with SHA256) subcommand")
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(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"))

View File

@@ -10,8 +10,9 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "piv-generate" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV Generate subcommand")
SubCommand::with_name(self.name()).about("PIV generate subcommand")
.arg(Arg::with_name("pin").short("p").long("pin").takes_value(true).help("OpenPGP card user pin"))
.arg(Arg::with_name("force").long("force").help("Force generate"))
.arg(Arg::with_name("json").long("json").help("JSON output"))
}
@@ -22,6 +23,10 @@ impl Command for CommandImpl {
warning!("This feature is not works");
let pin = opt_value_result!(sub_arg_matches.value_of("pin"), "User pin must be assigned");
if !sub_arg_matches.is_present("force") {
failure_and_exit!("--force must be assigned");
}
let mut yk = opt_result!(YubiKey::open(), "YubiKey not found: {}");
opt_result!(yk.verify_pin(pin.as_bytes()), "YubiKey verify pin failed: {}");

View File

@@ -15,7 +15,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "piv-sign" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV RSA Sign(with SHA256) subcommand")
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).default_value("123456").help("OpenPGP card user pin"))
.arg(Arg::with_name("sha256").short("2").long("sha256").takes_value(true).help("Digest SHA256 HEX"))

View File

@@ -16,7 +16,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "rsa-decrypt" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("RSA Decrypt subcommand")
SubCommand::with_name(self.name()).about("RSA decrypt subcommand")
.arg(Arg::with_name("pri-key-in").long("pri-key-in").takes_value(true).help("Private key in"))
.arg(Arg::with_name("encrypted").long("encrypted").takes_value(true).help("Encrypted data"))
.arg(Arg::with_name("padding").long("padding").takes_value(true)

View File

@@ -17,7 +17,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "rsa-encrypt" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("RSA Encrypt subcommand")
SubCommand::with_name(self.name()).about("RSA encrypt subcommand")
.arg(Arg::with_name("pub-key-in").long("pub-key-in").takes_value(true).help("Public key in"))
.arg(Arg::with_name("data").long("data").takes_value(true).help("Data"))
.arg(Arg::with_name("data-hex").long("data-hex").takes_value(true).help("Data in HEX"))

View File

@@ -21,7 +21,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "rsa-verify" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("RSA Verify subcommand")
SubCommand::with_name(self.name()).about("RSA verify subcommand")
.arg(Arg::with_name("pub-key-in").long("pub-key-in").takes_value(true).help("Public key in"))
.arg(Arg::with_name("signature").long("signature").takes_value(true).help("Signature HEX"))
.arg(Arg::with_name("in").short("i").long("in").takes_value(true).help("File in"))

View File

@@ -39,7 +39,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "sign-file" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV Sign(with SHA256) subcommand")
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("slot").short("s").long("slot")
.takes_value(true).required(true).help("PIV slot, e.g. 82, 83 ... 95, 9a, 9c, 9d, 9e"))

View File

@@ -24,7 +24,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "u2f-register" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("FIDO U2F Register subcommand")
SubCommand::with_name(self.name()).about("FIDO U2F register subcommand")
.arg(Arg::with_name("app-id").short("a").long("app-id").default_value("https://example.com").help("App id"))
.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"))

View File

@@ -24,7 +24,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "u2f-sign" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("FIDO U2F Sign subcommand")
SubCommand::with_name(self.name()).about("FIDO U2F sign subcommand")
.arg(Arg::with_name("app-id").short("a").long("app-id").default_value("https://example.com").help("App id"))
.arg(Arg::with_name("timeout").short("t").long("timeout").default_value("30").help("Timeout in seconds"))
.arg(Arg::with_name("public-key-hex").long("public-key-hex").takes_value(true).help("Public key hex"))

View File

@@ -21,7 +21,7 @@ impl Command for CommandImpl {
fn name(&self) -> &str { "verify-file" }
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name()).about("PIV Verify(with SHA256) subcommand")
SubCommand::with_name(self.name()).about("PIV verify(with SHA256) subcommand")
.arg(Arg::with_name("file").short("f").long("file").takes_value(true).required(false).help("Input file"))
.arg(Arg::with_name("sign-file").short("S").long("sign-file").takes_value(true).help("Sign file"))
}

View File

@@ -1,5 +1,6 @@
use base64::{DecodeError, Engine};
use base64::engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD};
use rust_util::XResult;
pub fn base64_encode<T: AsRef<[u8]>>(input: T) -> String {
STANDARD.encode(input)
@@ -12,3 +13,13 @@ pub fn base64_encode_url_safe_no_pad<T: AsRef<[u8]>>(input: T) -> String {
pub fn base64_decode<T: AsRef<[u8]>>(input: T) -> Result<Vec<u8>, DecodeError> {
STANDARD.decode(input)
}
pub fn try_decode(input: &str) -> XResult<Vec<u8>> {
return match hex::decode(input) {
Ok(v) => Ok(v),
Err(_) => match base64_decode(input) {
Ok(v) => Ok(v),
Err(e) => simple_error!("decode hex or base64 error: {}", e),
}
};
}