use crate::pkiutil::bytes_to_pem; use crate::{cmdutil, seutil, util}; use crate::util::base64_encode; use clap::{App, Arg, ArgMatches, SubCommand}; use p256::PublicKey; use rust_util::util_clap::{Command, CommandError}; use spki::DecodePublicKey; 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 generate 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("disable-bio") .long("disable-bio") .help("Disable bio"), ) .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); seutil::check_se_supported()?; let ty = sub_arg_matches.value_of("type").unwrap(); let host = sub_arg_matches.value_of("host").unwrap_or("macbook"); let sign = match ty { "signing" | "ecsign" | "sign" => true, "key_agreement" | "ecdh" | "dh" => false, _ => return simple_error!("Invalid type: {}", ty), }; let require_bio = !sub_arg_matches.is_present("disable-bio"); let (public_key_point, public_key_der, private_key) = seutil::generate_secure_enclave_p256_keypair(sign, require_bio)?; let key_uri = format!( "key://{}:se/p256:{}:{}", host, iff!(sign, "signing", "key_agreement"), private_key, ); print_se_key(json_output, &public_key_point, &public_key_der, &key_uri); Ok(None) } } pub fn print_se_key( json_output: bool, public_key_point: &[u8], public_key_der: &[u8], key_uri: &str, ) { let public_key_point_hex = hex::encode(public_key_point); let public_key_pem = bytes_to_pem("PUBLIC KEY", public_key_der); let public_key = PublicKey::from_public_key_pem(&public_key_pem).ok(); let public_key_jwk = public_key.map(|key| key.to_jwk_string()); if json_output { let mut json = BTreeMap::<&'_ str, String>::new(); json.insert("public_key_point", public_key_point_hex); json.insert("public_key_pem", base64_encode(public_key_der)); if let Some(public_key_jwk) = public_key_jwk { json.insert("public_key_jwk", base64_encode(public_key_jwk)); } json.insert("key_uri", key_uri.to_string()); util::print_pretty_json(&json); } else { success!("Public key(point): {}", public_key_point_hex); success!("Public key PEM: \n{}", public_key_pem); if let Some(public_key_jwk) = public_key_jwk { success!("Public key JWK: \n{}", &public_key_jwk); } success!("Key: {}", key_uri); } }