feat: v1.10.9, add se supprot

This commit is contained in:
2024-12-15 00:56:51 +08:00
parent 0fec0c25e7
commit bf9f228967
31 changed files with 3276 additions and 355 deletions

25
src/cmd_se.rs Normal file
View File

@@ -0,0 +1,25 @@
use crate::seutil;
use clap::{App, ArgMatches, SubCommand};
use rust_util::util_clap::{Command, CommandError};
pub struct CommandImpl;
impl Command for CommandImpl {
fn name(&self) -> &str {
"se"
}
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"))
}
fn run(&self, _arg_matches: &ArgMatches, _sub_arg_matches: &ArgMatches) -> CommandError {
if seutil::is_support_se() {
success!("Secure Enclave is supported.")
} else {
failure!("Secure Enclave is NOT supported.")
}
Ok(None)
}
}

73
src/cmd_se_generate.rs Normal file
View File

@@ -0,0 +1,73 @@
use crate::pkiutil::bytes_to_pem;
use crate::seutil;
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;
impl Command for CommandImpl {
fn name(&self) -> &str {
"se-generate"
}
fn subcommand<'a>(&self) -> App<'a, 'a> {
SubCommand::with_name(self.name())
.about("Secure Enclave subcommand")
.arg(
Arg::with_name("type")
.long("type")
.required(true)
.takes_value(true)
.help("Type signing or key_agreement"),
)
.arg(
Arg::with_name("host")
.long("host")
.required(false)
.takes_value(true)
.help("Host name"),
)
.arg(Arg::with_name("json").long("json").help("JSON output"))
}
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
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,
"key_agreement" | "ecdh" | "dh" => false,
_ => return simple_error!("Invalie type: {}", ty),
};
let (public_key_point, public_key_der, private_key) =
seutil::generate_secure_enclave_p256_keypair(sign)?;
let public_key_point_hex = hex::encode(&public_key_point);
let public_key_pem = bytes_to_pem("PUBLIC KEY", &*public_key_der);
let key = format!("key://{}:se/p256:{}:{}",
host, iff!(sign, "signing", "key_agreement"), private_key,
);
if json_output {
let mut json = BTreeMap::<&'_ str, String>::new();
json.insert("public_key_point", public_key_point_hex);
json.insert("public_key_pem", public_key_pem);
json.insert("key", key);
} else {
success!("Public key(point): {}", public_key_point_hex);
success!("Public key PEM: {}", public_key_pem);
success!("Key: {}", key);
}
Ok(None)
}
}

View File

@@ -4,56 +4,62 @@ extern crate rust_util;
use clap::{App, AppSettings, ArgMatches};
use rust_util::util_clap::{Command, CommandError};
mod util;
mod sshutil;
mod fido;
mod digest;
mod pivutil;
mod rsautil;
mod pkiutil;
mod hmacutil;
mod ecdsautil;
mod argsutil;
mod pgpcardutil;
mod cmd_list;
mod cmd_u2fregister;
mod cmd_u2fsign;
mod cmd_rsaencrypt;
mod cmd_rsadecrypt;
mod cmd_rsaverify;
#[cfg(feature = "with-sequoia-openpgp")]
mod cmd_pgp;
mod cmd_pgpcardadmin;
mod cmd_pgpcardlist;
mod cmd_pgpcardsign;
mod cmd_pgpcarddecrypt;
#[cfg(feature = "with-sequoia-openpgp")]
mod cmd_pgpcardmake;
mod cmd_piv;
mod cmd_pivsummary;
mod cmd_pivmeta;
mod cmd_pivverify;
mod cmd_pivrsasign;
mod cmd_pivecdh;
mod cmd_pivecsign;
mod cmd_pivdecrypt;
mod cmd_pivgenerate;
mod cmd_hmac_sha1;
mod cmd_chall;
mod cmd_challconfig;
mod cmd_sshagent;
mod cmd_sshparsesign;
mod cmd_sshpivsign;
mod cmd_sshpivcert;
mod cmd_sshpubkey;
mod cmd_sshparse;
mod cmd_hmac_sha1;
mod cmd_list;
#[cfg(feature = "with-sequoia-openpgp")]
mod cmd_pgp;
mod cmd_pgpageaddress;
mod cmd_signjwt;
mod cmd_pgpcardadmin;
mod cmd_pgpcarddecrypt;
mod cmd_pgpcardlist;
#[cfg(feature = "with-sequoia-openpgp")]
mod cmd_pgpcardmake;
mod cmd_pgpcardsign;
mod cmd_piv;
mod cmd_pivdecrypt;
mod cmd_pivecdh;
mod cmd_pivecsign;
mod cmd_pivgenerate;
mod cmd_pivmeta;
mod cmd_pivrsasign;
mod cmd_pivsummary;
mod cmd_pivverify;
mod cmd_rsadecrypt;
mod cmd_rsaencrypt;
mod cmd_rsaverify;
#[cfg(feature = "with-secure-enclave")]
mod cmd_se;
#[cfg(feature = "with-secure-enclave")]
mod cmd_se_generate;
mod cmd_signfile;
mod cmd_signjwt;
mod cmd_sshagent;
mod cmd_sshparse;
mod cmd_sshparsesign;
mod cmd_sshpivcert;
mod cmd_sshpivsign;
mod cmd_sshpubkey;
mod cmd_u2fregister;
mod cmd_u2fsign;
mod cmd_verifyfile;
mod signfile;
mod digest;
mod ecdhutil;
mod ecdsautil;
mod fido;
mod hmacutil;
mod pgpcardutil;
mod pinutil;
mod pivutil;
mod pkiutil;
mod rsautil;
#[cfg(feature = "with-secure-enclave")]
mod seutil;
mod signfile;
mod sshutil;
mod util;
pub struct DefaultCommandImpl;
@@ -117,11 +123,17 @@ fn inner_main() -> CommandError {
Box::new(cmd_signjwt::CommandImpl),
Box::new(cmd_signfile::CommandImpl),
Box::new(cmd_verifyfile::CommandImpl),
#[cfg(feature = "with-secure-enclave")]
Box::new(cmd_se::CommandImpl),
#[cfg(feature = "with-secure-enclave")]
Box::new(cmd_se_generate::CommandImpl),
];
let mut features: Vec<&str> = vec![];
#[cfg(feature = "with-sequoia-openpgp")]
features.push("with-sequoia-openpgp");
features.push("sequoia-openpgp");
#[cfg(feature = "with-secure-enclave")]
features.push("secure-enclave");
let about = format!(
"{}, features: [{}]",
"Card Cli is a command tool for WebAuthn, OpenPGP, YubiKey ... smart cards",
@@ -144,4 +156,4 @@ fn inner_main() -> CommandError {
}
}
DefaultCommandImpl::run(&matches)
}
}

46
src/seutil.rs Normal file
View File

@@ -0,0 +1,46 @@
use crate::util::base64_decode;
use rust_util::XResult;
use swift_rs::swift;
use swift_rs::{Bool, SRString};
swift!(fn is_support_secure_enclave() -> Bool);
swift!(fn generate_secure_enclave_p256_ecdh_keypair() -> SRString);
swift!(fn generate_secure_enclave_p256_ecsign_keypair() -> SRString);
swift!(fn compute_secure_enclave_p256_ecdh(private_key_base64: SRString, ephemera_public_key_base64: SRString) -> SRString);
pub fn is_support_se() -> bool {
unsafe { is_support_secure_enclave() }
}
pub fn generate_secure_enclave_p256_keypair(sign: bool) -> XResult<(Vec<u8>, Vec<u8>, String)> {
let p256_keypair_result = if sign {
unsafe { generate_secure_enclave_p256_ecsign_keypair() }
} else {
unsafe { generate_secure_enclave_p256_ecdh_keypair() }
};
let p256_keypair_result_str = p256_keypair_result.as_str();
if !p256_keypair_result_str.starts_with("ok:") {
return simple_error!(
"Generate P256 in secure enclave failed: {}",
p256_keypair_result_str
);
}
let public_key_and_private_key = p256_keypair_result_str.chars().skip(3).collect::<String>();
let public_key_and_private_keys = public_key_and_private_key.split(',').collect::<Vec<_>>();
if public_key_and_private_keys.len() != 3 {
return simple_error!(
"Generate P256 in secure enclave result is bad: {}",
public_key_and_private_key
);
}
let public_key_point = opt_result!(
base64_decode(public_key_and_private_keys[0]),
"Public key point is not base64 encoded: {}"
);
let public_key_der = opt_result!(
base64_decode(public_key_and_private_keys[1]),
"Public key der is not base64 encoded: {}"
);
let private_key = public_key_and_private_keys[2].to_string();
Ok((public_key_point, public_key_der, private_key))
}