feat: v1.12.0
This commit is contained in:
@@ -1,8 +1,16 @@
|
||||
use crate::util;
|
||||
use clap::{App, Arg, ArgMatches, SubCommand};
|
||||
use crate::cmd_sign_jwt::digest_by_jwt_algorithm;
|
||||
use crate::keyutil::{parse_key_uri, KeyUri, KeyUsage};
|
||||
use crate::pivutil::ToStr;
|
||||
use crate::util::{base64_decode, base64_encode};
|
||||
use crate::{cmdutil, pivutil, seutil, util};
|
||||
use clap::{App, ArgMatches, SubCommand};
|
||||
use jwt::AlgorithmType;
|
||||
use rust_util::util_clap::{Command, CommandError};
|
||||
use rust_util::XResult;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
use yubikey::piv::{sign_data, AlgorithmId};
|
||||
use yubikey::YubiKey;
|
||||
|
||||
pub struct CommandImpl;
|
||||
|
||||
@@ -14,41 +22,85 @@ impl Command for CommandImpl {
|
||||
fn subcommand<'a>(&self) -> App<'a, 'a> {
|
||||
SubCommand::with_name(self.name())
|
||||
.about("External sign subcommand")
|
||||
.arg(
|
||||
Arg::with_name("alg")
|
||||
.long("alg")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("Algorithm, e.g. RS256, RS384, RS512, ES256, ES384, ES512"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("parameter")
|
||||
.long("parameter")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("Parameter"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("message-base64")
|
||||
.long("message-base64")
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.help("Message in base64"),
|
||||
)
|
||||
.arg(cmdutil::build_alg_arg())
|
||||
.arg(cmdutil::build_parameter_arg())
|
||||
.arg(cmdutil::build_message_arg())
|
||||
.arg(cmdutil::build_pin_arg())
|
||||
}
|
||||
|
||||
fn run(&self, _arg_matches: &ArgMatches, sub_arg_matches: &ArgMatches) -> CommandError {
|
||||
let _alg = sub_arg_matches.value_of("alg").unwrap();
|
||||
let _parameter = sub_arg_matches.value_of("parameter").unwrap();
|
||||
let _message_base64 = sub_arg_matches.value_of("message-base64").unwrap();
|
||||
|
||||
// TODO do sign
|
||||
|
||||
let mut json = BTreeMap::new();
|
||||
json.insert("success", Value::Bool(true));
|
||||
json.insert("signature_base64", "**".into());
|
||||
match sign(sub_arg_matches) {
|
||||
Ok(signature_bytes) => {
|
||||
json.insert("success", Value::Bool(true));
|
||||
json.insert("signature_base64", base64_encode(&signature_bytes).into());
|
||||
}
|
||||
Err(e) => {
|
||||
json.insert("success", Value::Bool(false));
|
||||
json.insert("error", e.to_string().into());
|
||||
}
|
||||
}
|
||||
|
||||
util::print_pretty_json(&json);
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn sign(sub_arg_matches: &ArgMatches) -> XResult<Vec<u8>> {
|
||||
let alg = sub_arg_matches.value_of("alg").unwrap();
|
||||
let parameter = sub_arg_matches.value_of("parameter").unwrap();
|
||||
let message_base64 = sub_arg_matches.value_of("message-base64").unwrap();
|
||||
|
||||
let key_uri = parse_key_uri(parameter)?;
|
||||
let message_bytes = base64_decode(message_base64)?;
|
||||
match key_uri {
|
||||
KeyUri::SecureEnclaveKey(key) => {
|
||||
if key.usage != KeyUsage::Singing {
|
||||
simple_error!("Not singing key")
|
||||
} else {
|
||||
Ok(seutil::secure_enclave_p256_sign(
|
||||
&key.private_key,
|
||||
&message_bytes,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
KeyUri::YubikeyPivKey(key) => {
|
||||
let mut yk = opt_result!(YubiKey::open(), "Find YubiKey failed: {}");
|
||||
let pin_opt = pivutil::check_read_pin(&mut yk, key.slot, sub_arg_matches);
|
||||
|
||||
// FIXME Check Yubikey slot algorithm
|
||||
let jwt_algorithm = match alg {
|
||||
"ES256" => AlgorithmType::Es256,
|
||||
"ES384" => AlgorithmType::Es384,
|
||||
"RS256" => AlgorithmType::Rs256,
|
||||
_ => return simple_error!("Invalid alg: {}", alg),
|
||||
};
|
||||
|
||||
let is_p256_mismatch =
|
||||
key.algorithm == AlgorithmId::EccP256 && jwt_algorithm != AlgorithmType::Es256;
|
||||
let is_p384_mismatch =
|
||||
key.algorithm == AlgorithmId::EccP384 && jwt_algorithm != AlgorithmType::Es384;
|
||||
let is_rsa =
|
||||
key.algorithm == AlgorithmId::Rsa1024 || key.algorithm == AlgorithmId::Rsa2048;
|
||||
let is_rsa_mismatch = is_rsa && jwt_algorithm != AlgorithmType::Rs256;
|
||||
|
||||
if is_p256_mismatch || is_p384_mismatch || is_rsa_mismatch {
|
||||
return simple_error!("Invalid algorithm: {} vs {}", key.algorithm.to_str(), alg);
|
||||
}
|
||||
|
||||
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?;
|
||||
|
||||
if let Some(pin) = pin_opt {
|
||||
opt_result!(
|
||||
yk.verify_pin(pin.as_bytes()),
|
||||
"YubiKey verify pin failed: {}"
|
||||
);
|
||||
}
|
||||
let signed_data = opt_result!(
|
||||
sign_data(&mut yk, &raw_in, key.algorithm, key.slot),
|
||||
"Sign YubiKey failed: {}"
|
||||
);
|
||||
Ok(signed_data.to_vec())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user