feat: v1.13.0

This commit is contained in:
2025-05-07 23:54:20 +08:00
parent 06d2da4ddf
commit a1ae0ff4dc
11 changed files with 193 additions and 65 deletions

View File

@@ -33,8 +33,14 @@ impl Command for CommandImpl {
}
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();
let message_bytes = base64_decode(message_base64)?;
let mut json = BTreeMap::new();
match sign(sub_arg_matches) {
let key_uri = parse_key_uri(parameter)?;
match sign(alg, &message_bytes, key_uri, sub_arg_matches) {
Ok(signature_bytes) => {
json.insert("success", Value::Bool(true));
json.insert("signature_base64", base64_encode(&signature_bytes).into());
@@ -50,13 +56,7 @@ impl Command for CommandImpl {
}
}
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)?;
pub fn sign(alg: &str, message: &[u8], key_uri: KeyUri, sub_arg_matches: &ArgMatches) -> XResult<Vec<u8>> {
match key_uri {
KeyUri::SecureEnclaveKey(key) => {
if "ES256" != alg {
@@ -65,7 +65,7 @@ fn sign(sub_arg_matches: &ArgMatches) -> XResult<Vec<u8>> {
if key.usage != KeyUsage::Singing {
return simple_error!("Not singing key");
}
seutil::secure_enclave_p256_sign(&key.private_key, &message_bytes)
seutil::secure_enclave_p256_sign(&key.private_key, message)
}
KeyUri::YubikeyPivKey(key) => {
let mut yk = yubikeyutil::open_yubikey_with_args(sub_arg_matches)?;
@@ -86,7 +86,7 @@ fn sign(sub_arg_matches: &ArgMatches) -> XResult<Vec<u8>> {
"Yubikey not supported algorithm: {}",
key.algorithm.to_str()
);
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?;
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, message)?;
let signed_data = opt_result!(
sign_data(&mut yk, &raw_in, algorithm, key.slot),
"Sign YubiKey failed: {}"
@@ -98,7 +98,7 @@ fn sign(sub_arg_matches: &ArgMatches) -> XResult<Vec<u8>> {
let private_key = cmd_hmac_decrypt::try_decrypt(&key.hmac_enc_private_key)?;
let (jwt_algorithm, private_key_d) = parse_ecdsa_private_key(&private_key)?;
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, &message_bytes)?;
let raw_in = digest_by_jwt_algorithm(jwt_algorithm, message)?;
let ecdsa_algorithm = convert_jwt_algorithm_to_ecdsa_algorithm(jwt_algorithm)?;
let signed_data = ecdsautil::ecdsa_sign(
ecdsa_algorithm,
@@ -116,7 +116,7 @@ fn sign(sub_arg_matches: &ArgMatches) -> XResult<Vec<u8>> {
let rsa_sign_algorithm =
opt_value_result!(RsaSignAlgorithm::from_str(alg), "Invalid --alg: {}", alg);
rsautil::sign(&rsa_private_key, rsa_sign_algorithm, &message_bytes)
rsautil::sign(&rsa_private_key, rsa_sign_algorithm, message)
} else {
simple_error!("Invalid algorithm: {}", key.algorithm.to_str())
}

85
src/cmd_sign_jwt.rs Normal file
View File

@@ -0,0 +1,85 @@
use crate::cmd_sign_jwt_piv::{
build_jwt_parts, fill_sign_jwt_app_args, merge_header_claims,
merge_payload_claims, print_jwt_token,
};
use crate::ecdsautil::parse_ecdsa_to_rs;
use crate::keyutil::parse_key_uri;
use crate::{cmd_external_sign, cmdutil, util};
use clap::{App, ArgMatches, SubCommand};
use jwt::ToBase64;
use jwt::{AlgorithmType, Header};
use rust_util::util_clap::{Command, CommandError};
use rust_util::XResult;
use serde_json::{Map, Value};
use crate::pivutil::ToStr;
const SEPARATOR: &str = ".";
pub struct CommandImpl;
impl Command for CommandImpl {
fn name(&self) -> &str {
"sign-jwt"
}
fn subcommand<'a>(&self) -> App<'a, 'a> {
let app = SubCommand::with_name(self.name())
.about("Sign JWT subcommand")
.arg(cmdutil::build_key_uri_arg().required(false))
.arg(cmdutil::build_parameter_arg().required(false))
.arg(cmdutil::build_pin_arg())
.arg(cmdutil::build_serial_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 = cmdutil::check_json_output(sub_arg_matches);
let (header, payload, jwt_claims) = build_jwt_parts(sub_arg_matches)?;
let token_string = sign_jwt(header, &payload, &jwt_claims, sub_arg_matches)?;
print_jwt_token(json_output, token_string);
Ok(None)
}
}
fn sign_jwt(
mut header: Header,
payload: &Option<String>,
claims: &Map<String, Value>,
sub_arg_matches: &ArgMatches,
) -> XResult<String> {
let key = match sub_arg_matches.value_of("key") {
Some(key) => key,
None => match sub_arg_matches.value_of("parameter") {
Some(parameter) => parameter,
None => return simple_error!("Parameter --key or --parameter required"),
}
};
let key_uri = parse_key_uri(key)?;
let jwt_algorithm = key_uri.get_preferred_algorithm_type();
header.algorithm = jwt_algorithm;
debugging!("Header: {:?}", header);
debugging!("Claims: {:?}", claims);
let header = opt_result!(header.to_base64(), "Header to base64 failed: {}");
let claims = merge_payload_claims(payload, claims)?;
let tobe_signed = merge_header_claims(header.as_bytes(), claims.as_bytes());
let signature = cmd_external_sign::sign(jwt_algorithm.to_str(), &tobe_signed, key_uri, sub_arg_matches)?;
let signed_data = match jwt_algorithm {
AlgorithmType::Rs256 => signature,
AlgorithmType::Es256 | AlgorithmType::Es384 | AlgorithmType::Es512 => {
parse_ecdsa_to_rs(signature.as_slice())?
}
_ => return simple_error!("SHOULD NOT HAPPEN: {:?}", jwt_algorithm),
};
let signature = util::base64_encode_url_safe_no_pad(&signed_data);
Ok([&*header, &*claims, &signature].join(SEPARATOR))
}

View File

@@ -19,11 +19,11 @@ pub struct CommandImpl;
impl Command for CommandImpl {
fn name(&self) -> &str {
"sign-jwt"
"sign-jwt-piv"
}
fn subcommand<'a>(&self) -> App<'a, 'a> {
let app = SubCommand::with_name(self.name()).about("Sign JWT subcommand")
let app = SubCommand::with_name(self.name()).about("Sign JWT(PIV) subcommand")
.arg(cmdutil::build_slot_arg())
.arg(cmdutil::build_pin_arg())
.arg(cmdutil::build_no_pin_arg())

View File

@@ -21,7 +21,7 @@ impl Command for CommandImpl {
}
fn subcommand<'a>(&self) -> App<'a, 'a> {
let app = SubCommand::with_name(self.name()).about("Sign JWT subcommand")
let app = SubCommand::with_name(self.name()).about("Sign JWT(SE) subcommand")
.arg(cmdutil::build_key_uri_arg())
.arg(cmdutil::build_json_arg());
cmd_sign_jwt_piv::fill_sign_jwt_app_args(app)

View File

@@ -19,7 +19,7 @@ impl Command for CommandImpl {
}
fn subcommand<'a>(&self) -> App<'a, 'a> {
let app = SubCommand::with_name(self.name()).about("Sign JWT subcommand")
let app = SubCommand::with_name(self.name()).about("Sign JWT(Soft EC) subcommand")
.arg(Arg::with_name("private-key").short("k").long("private-key").takes_value(true).help("Private key PKCS#8"))
.arg(cmdutil::build_json_arg());
cmd_sign_jwt_piv::fill_sign_jwt_app_args(app)

View File

@@ -54,10 +54,14 @@ pub fn parse_ecdsa_r_and_s(signature_der: &[u8]) -> XResult<(Vec<u8>, Vec<u8>)>
Ok((vec_r, vec_s))
}
const P256_LEN: usize = 32;
const P384_LEN: usize = 48;
const P521_LEN: usize = 66;
fn trim_ecdsa_point_coord(p: &[u8]) -> Vec<u8> {
if p.len() == ((256 / 8) + 1) || p.len() == ((384 / 8) + 1) {
if p.len() == (P256_LEN + 1) || p.len() == (P384_LEN + 1) || p.len() == (P521_LEN + 1) {
p[1..].to_vec()
} else if p.len() == ((256 / 8) - 1) || p.len() == ((384 / 8) - 1) {
} else if p.len() == (P256_LEN - 1) || p.len() == (P384_LEN - 1) || p.len() == (P521_LEN - 1) {
let mut v = vec![];
v.push(0_u8);
v.extend_from_slice(p);

View File

@@ -1,3 +1,4 @@
use jwt::AlgorithmType;
use crate::pivutil::{FromStr, ToStr};
use regex::Regex;
use rust_util::XResult;
@@ -18,6 +19,21 @@ impl KeyUri {
_ => simple_error!("Not a secure enclave key."),
}
}
pub fn get_preferred_algorithm_type(&self) -> AlgorithmType {
let algorithm_id = match &self {
KeyUri::SecureEnclaveKey(_) => return AlgorithmType::Es256,
KeyUri::YubikeyPivKey(key) => key.algorithm,
KeyUri::YubikeyHmacEncSoftKey(key) => key.algorithm,
};
match algorithm_id {
KeyAlgorithmId::Rsa1024 | KeyAlgorithmId::Rsa2048
| KeyAlgorithmId::Rsa3072 | KeyAlgorithmId::Rsa4096 => AlgorithmType::Rs256,
KeyAlgorithmId::EccP256 => AlgorithmType::Es256,
KeyAlgorithmId::EccP384 => AlgorithmType::Es384,
KeyAlgorithmId::EccP521 => AlgorithmType::Es512,
}
}
}
impl ToString for KeyUri {

View File

@@ -49,6 +49,7 @@ mod cmd_se_ecdh;
mod cmd_se_ecsign;
mod cmd_se_generate;
mod cmd_se_recover;
mod cmd_sign_jwt;
mod cmd_sign_jwt_piv;
mod cmd_sign_jwt_se;
mod cmd_sign_jwt_soft;
@@ -69,6 +70,7 @@ mod fidoutil;
mod hmacutil;
mod keychain;
mod keyutil;
mod pbeutil;
mod pgpcardutil;
mod pinutil;
mod pivutil;
@@ -79,7 +81,6 @@ mod signfile;
mod sshutil;
mod util;
mod yubikeyutil;
mod pbeutil;
pub struct DefaultCommandImpl;
@@ -145,6 +146,7 @@ fn inner_main() -> CommandError {
Box::new(cmd_sign_jwt_piv::CommandImpl),
Box::new(cmd_sign_jwt_soft::CommandImpl),
Box::new(cmd_sign_jwt_se::CommandImpl),
Box::new(cmd_sign_jwt::CommandImpl),
Box::new(cmd_file_sign::CommandImpl),
Box::new(cmd_file_verify::CommandImpl),
Box::new(cmd_se::CommandImpl),

View File

@@ -1,4 +1,5 @@
use clap::ArgMatches;
use jwt::AlgorithmType;
use rust_util::XResult;
use spki::{ObjectIdentifier, SubjectPublicKeyInfoOwned};
use spki::der::{Decode, Encode};
@@ -62,6 +63,26 @@ pub trait FromStr {
Self: Sized;
}
impl ToStr for AlgorithmType {
fn to_str(&self) -> &str {
match self {
AlgorithmType::Hs256 => "HS256",
AlgorithmType::Hs384 => "HS384",
AlgorithmType::Hs512 => "HS512",
AlgorithmType::Rs256 => "RS256",
AlgorithmType::Rs384 => "RS384",
AlgorithmType::Rs512 => "RS512",
AlgorithmType::Es256 => "ES256",
AlgorithmType::Es384 => "ES384",
AlgorithmType::Es512 => "ES512",
AlgorithmType::Ps256 => "PS256",
AlgorithmType::Ps384 => "PS384",
AlgorithmType::Ps512 => "PS512",
AlgorithmType::None => "NONE",
}
}
}
impl ToStr for PinPolicy {
fn to_str(&self) -> &str {
match self {