From 656785b8763d4704db97684b6549be9a4a72e8ac Mon Sep 17 00:00:00 2001 From: unknown <1592085657@qq.com> Date: Fri, 7 Nov 2025 16:18:37 +0800 Subject: [PATCH 1/2] 1.support cms signature 2.support import cert ui 3.supoort sm2 key and sm2 hash algo --- Cargo.lock | 4 +- app/src/pages/listShow/ChangeCheck.vue | 2 +- app/src/pages/listShow/CreateX509.vue | 20 ++++ app/src/pages/listShow/ImportX509.vue | 18 +++- scripts/initialize-user-and-keys.sh | 26 +++++ src/client/cmd/add.rs | 1 + src/client/file_handler/factory.rs | 2 + src/client/file_handler/generic_cms.rs | 141 +++++++++++++++++++++++++ src/client/file_handler/ima.rs | 2 +- src/client/file_handler/mod.rs | 1 + src/domain/datakey/plugins/x509.rs | 40 +++++++ src/infra/sign_plugin/x509.rs | 122 ++++++++++++++++----- src/util/sign.rs | 2 + 13 files changed, 350 insertions(+), 31 deletions(-) create mode 100644 src/client/file_handler/generic_cms.rs diff --git a/Cargo.lock b/Cargo.lock index 1e9a28a..d5dec6f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3030,9 +3030,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-src" -version = "300.1.6+3.1.4" +version = "300.5.3+3.5.4" +checksum = "dc6bad8cd0233b63971e232cc9c5e83039375b8586d2312f31fda85db8f888c2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439fac53e092cd7442a3660c85dde4643ab3b5bd39040912388dcdabf6b88085" dependencies = [ "cc", ] diff --git a/app/src/pages/listShow/ChangeCheck.vue b/app/src/pages/listShow/ChangeCheck.vue index d403a64..a4d6eab 100644 --- a/app/src/pages/listShow/ChangeCheck.vue +++ b/app/src/pages/listShow/ChangeCheck.vue @@ -28,7 +28,7 @@ import { useBaseStore } from "@/store/base"; const useBase = useBaseStore(); const title = ref(); const check = [ - // "Import X509 Keys", + "Import X509 Keys", // "Import PGP Keys", "Create X509 Keys", "Create PGP Keys", diff --git a/app/src/pages/listShow/CreateX509.vue b/app/src/pages/listShow/CreateX509.vue index 1435952..7469729 100644 --- a/app/src/pages/listShow/CreateX509.vue +++ b/app/src/pages/listShow/CreateX509.vue @@ -114,6 +114,10 @@ :key="item.value" :label="item.label" :value="item.value" + :disabled=" + (formLabelAlign.key_type === 'sm2' && ['2048', '3072', '4096'].includes(item.value)) || + (['rsa', 'dsa'].includes(formLabelAlign.key_type) && ['256'].includes(item.value)) + " /> @@ -128,6 +132,10 @@ :key="item.value" :label="item.label" :value="item.value" + :disabled=" + (formLabelAlign.key_type === 'sm2' && ['md5', 'sha1', 'sha2_256', 'sha2_224', 'sha2_384', 'sha2_512'].includes(item.value)) || + (['rsa', 'dsa'].includes(formLabelAlign.key_type) && ['sm3'].includes(item.value)) + " /> @@ -262,6 +270,10 @@ const optionsType = [ value: 'dsa', label: 'DSA', }, + { + value: 'sm2', + label: 'SM2', + }, ]; const typeKey = [ { @@ -283,6 +295,10 @@ const usageKey = [ }, ]; const optionsSize = [ + { + value: '256', + label: '256', + }, { value: '2048', label: '2048', @@ -321,6 +337,10 @@ const optionsDigest = [ value: 'sha2_512', label: 'sha2_512', }, + { + value: 'sm3', + label: 'sm3', + }, ]; //删除掉 diff --git a/app/src/pages/listShow/ImportX509.vue b/app/src/pages/listShow/ImportX509.vue index 5504490..0351f0e 100644 --- a/app/src/pages/listShow/ImportX509.vue +++ b/app/src/pages/listShow/ImportX509.vue @@ -122,7 +122,7 @@ const formLabelAlign = reactive({ private_key: "", public_key: "", certificate: "", - key_type: "x509", + key_type: "x509ee", keytype: "rsa", key_length: "2048", digest_algorithm: "md5", @@ -146,14 +146,22 @@ const param = ref({ const optionsType = [ { value: "rsa", - label: "RSA", + label: "Rsa", }, { value: "dsa", - label: "DSA", + label: "Dsa", }, + { + value: "sm2", + label: "SM2", + } ]; const optionsSize = [ + { + value: '256', + label: '256', + }, { value: "2048", label: "2048", @@ -192,6 +200,10 @@ const optionsDigest = [ value: "sha2_512", label: "sha2_512", }, + { + value: "sm3", + label: "sm3", + }, ]; // 表单校验规则 /* 姓名 */ diff --git a/scripts/initialize-user-and-keys.sh b/scripts/initialize-user-and-keys.sh index 9b030f8..5aa974f 100755 --- a/scripts/initialize-user-and-keys.sh +++ b/scripts/initialize-user-and-keys.sh @@ -38,6 +38,24 @@ function create_default_x509_ee { --param-x509-common-name Infra --param-x509-organization Huawei --param-x509-locality ShenZhen --param-x509-province-name GuangDong --param-x509-country-name CN --param-x509-organizational-unit "Infra EE" --digest-algorithm sha2_256 --param-x509-parent-name default-x509ica --visibility public } +function create_default_x509_ca_sm2 { + echo "start to create default x509_sm2 CA identified with default-x509ca-sm2" + RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-x509ca-sm2 --description "used for test purpose only" --key-type x509ca --email tommylikehu@gmail.com --param-key-type sm2 --param-key-size 2048 \ + --param-x509-common-name Infra --param-x509-organization Huawei --param-x509-locality ShenZhen --param-x509-province-name GuangDong --param-x509-country-name CN --param-x509-organizational-unit "Infra CA" --digest-algorithm sm3 --visibility public +} + +function create_default_x509_ia_sm2 { + echo "start to create default x509 sm2 ICA identified with default-x509" + RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-x509ica-sm2 --description "used for test purpose only" --key-type x509ica --email tommylikehu@gmail.com --param-key-type sm2 --param-key-size 2048 \ + --param-x509-common-name Infra --param-x509-organization Huawei --param-x509-locality ShenZhen --param-x509-province-name GuangDong --param-x509-country-name CN --param-x509-organizational-unit "Infra ICA" --digest-algorithm sm3 --param-x509-parent-name default-x509ca-sm2 --visibility public +} + +function create_default_x509_ee_sm2 { + echo "start to create default x509 sm2 EE certificate identified with default-x509" + RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-x509ee-sm2 --description "used for test purpose only" --key-type x509ee --email tommylikehu@gmail.com --param-key-type sm2 --param-key-size 2048 \ + --param-x509-common-name Infra --param-x509-organization Huawei --param-x509-locality ShenZhen --param-x509-province-name GuangDong --param-x509-country-name CN --param-x509-organizational-unit "Infra EE" --digest-algorithm sm3 --param-x509-parent-name default-x509ica-sm2 --visibility public +} + function create_default_openpgp_rsa { echo "start to create default openpgp keys identified with default-pgp" RUST_LOG=info ./target/debug/control-admin --config ./config/server.toml generate-keys --name default-pgp-rsa --description "used for test purpose only" --key-type pgp --email tommylikehu@gmail.com --param-key-type rsa --param-key-size 2048 --param-pgp-email infra@openeuler.org --param-pgp-passphrase husheng1234 --digest-algorithm sha2_256 --visibility public @@ -54,6 +72,8 @@ function create_default_private_openpgp_rsa { } + + echo "Preparing basic keys for signatrust......" check-binary @@ -68,6 +88,12 @@ create_default_x509_ica create_default_x509_ee +create_default_x509_ca_sm2 + +create_default_x509_ia_sm2 + +create_default_x509_ee_sm2 + create_default_openpgp_rsa create_default_openpgp_eddsa diff --git a/src/client/cmd/add.rs b/src/client/cmd/add.rs index 9a8a5f8..e1ab3c8 100644 --- a/src/client/cmd/add.rs +++ b/src/client/cmd/add.rs @@ -51,6 +51,7 @@ lazy_static! { (FileType::EfiImage, vec![".*"]), // ima can be used for any file (FileType::ImaEvm, vec![".*"]), + (FileType::GenericCms, vec![".*"]), ]); } diff --git a/src/client/file_handler/factory.rs b/src/client/file_handler/factory.rs index 537fa9b..7a1175e 100644 --- a/src/client/file_handler/factory.rs +++ b/src/client/file_handler/factory.rs @@ -16,6 +16,7 @@ use super::efi::EfiFileHandler; use super::generic::GenericFileHandler; +use super::generic_cms::CmsFileHandler; use super::ima::ImaFileHandler; use super::kernel_module::KernelModuleFileHandler; use super::rpm::RpmFileHandler; @@ -32,6 +33,7 @@ impl FileHandlerFactory { FileType::KernelModule => Box::new(KernelModuleFileHandler::new()), FileType::EfiImage => Box::new(EfiFileHandler::new()), FileType::ImaEvm => Box::new(ImaFileHandler::new()), + FileType::GenericCms => Box::new(CmsFileHandler::new()), } } } diff --git a/src/client/file_handler/generic_cms.rs b/src/client/file_handler/generic_cms.rs new file mode 100644 index 0000000..d843fb3 --- /dev/null +++ b/src/client/file_handler/generic_cms.rs @@ -0,0 +1,141 @@ +/* + * + * * // Copyright (c) 2025 Huawei Technologies Co.,Ltd. All rights reserved. + * * // + * * // signatrust is licensed under Mulan PSL v2. + * * // You can use this software according to the terms and conditions of the Mulan + * * // PSL v2. + * * // You may obtain a copy of Mulan PSL v2 at: + * * // http://license.coscl.org.cn/MulanPSL2 + * * // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY + * * // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO + * * // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. + * * // See the Mulan PSL v2 for more details. + * + */ + +use super::traits::FileHandler; +use crate::util::error::{Error, Result}; +use crate::util::options; +use crate::util::sign::{KeyType, SignType}; +use async_trait::async_trait; +use std::collections::HashMap; +use std::path::PathBuf; +use tokio::fs; +use uuid::Uuid; + +const CMS_EXTENSION: &str = "p7s"; + +#[derive(Clone)] +pub struct CmsFileHandler {} + +impl CmsFileHandler { + pub fn new() -> Self { + Self {} + } +} + +#[async_trait] +impl FileHandler for CmsFileHandler { + fn validate_options(&self, sign_options: &mut HashMap) -> Result<()> { + if let Some(detached) = sign_options.get(options::DETACHED) { + if detached == "false" { + return Err(Error::InvalidArgumentError( + "generic-cms only support detached signature".to_string(), + )); + } + } + + if let Some(key_type) = sign_options.get(options::KEY_TYPE) { + if key_type != KeyType::X509EE.to_string().as_str() { + return Err(Error::InvalidArgumentError( + "generic-cms only support x509 key".to_string(), + )); + } + } + + if let Some(sign_type) = sign_options.get(options::SIGN_TYPE) { + if sign_type != SignType::Cms.to_string().as_str() { + return Err(Error::InvalidArgumentError( + "generic-cms file only support cms".to_string(), + )); + } + } + Ok(()) + } + + async fn assemble_data( + &self, + path: &PathBuf, + data: Vec>, + temp_dir: &PathBuf, + _sign_options: &HashMap, + _key_attributes: &HashMap, + ) -> Result<(String, String)> { + let temp_file = temp_dir.join(Uuid::new_v4().to_string()); + fs::write(temp_file.clone(), &data[0]).await?; + Ok(( + temp_file.as_path().display().to_string(), + format!("{}.{}", path.as_path().display(), CMS_EXTENSION), + )) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::env; + + #[test] + fn test_validate_options() { + let mut options = HashMap::new(); + options.insert(options::DETACHED.to_string(), "false".to_string()); + let handler = CmsFileHandler::new(); + let result = handler.validate_options(&mut options); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "invalid argument: generic-cms only support detached signature" + ); + + options.insert(options::DETACHED.to_string(), "true".to_string()); + options.insert(options::KEY_TYPE.to_string(), KeyType::Pgp.to_string()); + let result = handler.validate_options(&mut options); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "invalid argument: generic-cms only support x509 key" + ); + + options.insert(options::KEY_TYPE.to_string(), KeyType::X509EE.to_string()); + options.insert( + options::SIGN_TYPE.to_string(), + SignType::RsaHash.to_string(), + ); + let result = handler.validate_options(&mut options); + assert!(result.is_err()); + assert_eq!( + result.unwrap_err().to_string(), + "invalid argument: generic-cms file only support cms" + ); + options.insert(options::SIGN_TYPE.to_string(), SignType::Cms.to_string()); + let result = handler.validate_options(&mut options); + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_assemble_data() { + let handler = CmsFileHandler::new(); + let options = HashMap::new(); + let path = PathBuf::from("./test_data/test.txt"); + let data = vec![vec![1, 2, 3]]; + let temp_dir = env::temp_dir(); + let result = handler + .assemble_data(&path, data, &temp_dir, &options, &HashMap::new()) + .await; + assert!(result.is_ok()); + let (temp_file, file_name) = result.expect("invoke assemble data should work"); + assert_eq!(temp_file.starts_with(temp_dir.to_str().unwrap()), true); + assert_eq!(file_name, "./test_data/test.txt.p7s"); + } +} diff --git a/src/client/file_handler/ima.rs b/src/client/file_handler/ima.rs index 1ccdd44..74b6708 100644 --- a/src/client/file_handler/ima.rs +++ b/src/client/file_handler/ima.rs @@ -82,7 +82,7 @@ impl FileHandler for ImaFileHandler { if let Some(detached) = sign_options.get(options::DETACHED) { if detached == "false" { return Err(Error::InvalidArgumentError( - "ima signer only support detached signature, you may need remove the --detach argument".to_string(), + "ima signer only support detached signature, you may need add the --detach argument".to_string(), )); } } diff --git a/src/client/file_handler/mod.rs b/src/client/file_handler/mod.rs index 21ccba3..c9b6c23 100644 --- a/src/client/file_handler/mod.rs +++ b/src/client/file_handler/mod.rs @@ -1,6 +1,7 @@ pub mod efi; pub mod factory; pub mod generic; +pub mod generic_cms; pub mod ima; pub mod kernel_module; pub mod rpm; diff --git a/src/domain/datakey/plugins/x509.rs b/src/domain/datakey/plugins/x509.rs index 63dcfbf..cece712 100644 --- a/src/domain/datakey/plugins/x509.rs +++ b/src/domain/datakey/plugins/x509.rs @@ -14,7 +14,9 @@ use crate::util::error::{Error, Result}; use enum_iterator::Sequence; use openssl::dsa::Dsa; +use openssl::ec::{EcGroup, EcKey}; use openssl::hash::MessageDigest; +use openssl::nid::Nid; use openssl::pkey::{PKey, Private}; use openssl::rsa::Rsa; use serde::Deserialize; @@ -23,6 +25,7 @@ use std::fmt::{Display, Formatter}; use std::str::FromStr; pub const X509_VALID_KEY_SIZE: [&str; 3] = ["2048", "3072", "4096"]; +pub const X509_SM2_VALID_KEY_SIZE: [&str; 1] = ["256"]; #[derive(Debug, Clone, PartialEq, Sequence, Deserialize, Default)] pub enum X509EEUsage { @@ -58,6 +61,8 @@ pub enum X509KeyType { Rsa, #[serde(rename = "dsa")] Dsa, + #[serde(rename = "sm2")] + Sm2, } impl FromStr for X509KeyType { @@ -67,6 +72,7 @@ impl FromStr for X509KeyType { match s { "rsa" => Ok(X509KeyType::Rsa), "dsa" => Ok(X509KeyType::Dsa), + "sm2" => Ok(X509KeyType::Sm2), _ => Err(Error::UnsupportedTypeError(format!( "unsupported x509 key type {}", s @@ -80,15 +86,32 @@ impl Display for X509KeyType { match self { X509KeyType::Rsa => write!(f, "rsa"), X509KeyType::Dsa => write!(f, "dsa"), + X509KeyType::Sm2 => write!(f, "sm2"), } } } +pub fn generate_sm2() -> Result> { + let group = EcGroup::from_curve_name(Nid::SM2)?; + let ec_key = EcKey::generate(&group)?; + let key = PKey::from_ec_key(ec_key)?; + Ok(key) +} + impl X509KeyType { pub fn get_real_key_type(&self, key_length: u32) -> Result> { match self { X509KeyType::Rsa => Ok(PKey::from_rsa(Rsa::generate(key_length)?)?), X509KeyType::Dsa => Ok(PKey::from_dsa(Dsa::generate(key_length)?)?), + X509KeyType::Sm2 => Ok(generate_sm2()?), // sm2 key length is fixed at 256 + } + } + + pub fn as_str(&self) -> &'static str { + match self { + X509KeyType::Rsa => "rsa", + X509KeyType::Dsa => "dsa", + X509KeyType::Sm2 => "sm2", } } } @@ -107,6 +130,8 @@ pub enum X509DigestAlgorithm { SHA2_384, #[serde(rename = "sha2_512")] SHA2_512, + #[serde(rename = "sm3")] + SM3, } impl Display for X509DigestAlgorithm { @@ -118,6 +143,7 @@ impl Display for X509DigestAlgorithm { X509DigestAlgorithm::SHA2_256 => write!(f, "sha2_256"), X509DigestAlgorithm::SHA2_384 => write!(f, "sha2_384"), X509DigestAlgorithm::SHA2_512 => write!(f, "sha2_512"), + X509DigestAlgorithm::SM3 => write!(f, "sm3"), } } } @@ -133,6 +159,7 @@ impl FromStr for X509DigestAlgorithm { "sha2_256" => Ok(X509DigestAlgorithm::SHA2_256), "sha2_384" => Ok(X509DigestAlgorithm::SHA2_384), "sha2_512" => Ok(X509DigestAlgorithm::SHA2_512), + "sm3" => Ok(X509DigestAlgorithm::SM3), _ => Err(Error::UnsupportedTypeError(format!( "unsupported x509 digest algorithm {}", s @@ -150,6 +177,19 @@ impl X509DigestAlgorithm { X509DigestAlgorithm::SHA2_256 => MessageDigest::sha256(), X509DigestAlgorithm::SHA2_384 => MessageDigest::sha384(), X509DigestAlgorithm::SHA2_512 => MessageDigest::sha512(), + X509DigestAlgorithm::SM3 => MessageDigest::sm3(), + } + } + + pub fn as_str(&self) -> &'static str { + match self { + X509DigestAlgorithm::MD5 => "md5", + X509DigestAlgorithm::SHA1 => "sha1", + X509DigestAlgorithm::SHA2_224 => "sha2_224", + X509DigestAlgorithm::SHA2_256 => "sha2_256", + X509DigestAlgorithm::SHA2_384 => "sha2_384", + X509DigestAlgorithm::SHA2_512 => "sha2_512", + X509DigestAlgorithm::SM3 => "sm3", } } } diff --git a/src/infra/sign_plugin/x509.rs b/src/infra/sign_plugin/x509.rs index a497e6e..b67fb18 100644 --- a/src/infra/sign_plugin/x509.rs +++ b/src/infra/sign_plugin/x509.rs @@ -14,10 +14,6 @@ * */ -use std::collections::HashMap; -use std::str::FromStr; -use std::time::{Duration, SystemTime}; - use chrono::{DateTime, Utc}; use foreign_types_shared::{ForeignType, ForeignTypeRef}; use openssl::asn1::{Asn1Integer, Asn1Time}; @@ -40,6 +36,9 @@ use openssl_sys::{ }; use secstr::SecVec; use serde::Deserialize; +use std::collections::HashMap; +use std::str::FromStr; +use std::time::{Duration, SystemTime}; use super::util::{attributes_validate, validate_utc_time, validate_utc_time_not_expire}; use crate::domain::datakey::entity::{ @@ -47,7 +46,7 @@ use crate::domain::datakey::entity::{ INFRA_CONFIG_DOMAIN_NAME, }; use crate::domain::datakey::plugins::x509::{ - X509DigestAlgorithm, X509EEUsage, X509KeyType, X509_VALID_KEY_SIZE, + X509DigestAlgorithm, X509EEUsage, X509KeyType, X509_SM2_VALID_KEY_SIZE, X509_VALID_KEY_SIZE, }; use crate::domain::sign_plugin::SignPlugins; use crate::util::attributes; @@ -60,6 +59,7 @@ use enum_iterator::all; use validator::{Validate, ValidationError}; #[derive(Debug, Validate, Deserialize)] +#[validate(schema(function = "validate_x509_key_size_for_generation"))] pub struct X509KeyGenerationParameter { #[validate(length(min = 1, max = 30, message = "invalid x509 subject 'CommonName'"))] common_name: String, @@ -82,10 +82,6 @@ pub struct X509KeyGenerationParameter { #[validate(length(min = 2, max = 2, message = "invalid x509 subject 'CountryName'"))] country_name: String, key_type: X509KeyType, - #[validate(custom( - function = "validate_x509_key_size", - message = "invalid x509 attribute 'key_length'" - ))] key_length: String, digest_algorithm: X509DigestAlgorithm, #[validate(custom( @@ -103,12 +99,9 @@ pub struct X509KeyGenerationParameter { } #[derive(Debug, Validate, Deserialize)] +#[validate(schema(function = "validate_x509_key_size_for_import"))] pub struct X509KeyImportParameter { key_type: X509KeyType, - #[validate(custom( - function = "validate_x509_key_size", - message = "invalid x509 attribute 'key_length'" - ))] key_length: String, digest_algorithm: X509DigestAlgorithm, #[validate(custom( @@ -136,13 +129,42 @@ impl X509KeyGenerationParameter { } } -fn validate_x509_key_size(key_size: &str) -> std::result::Result<(), ValidationError> { - if !X509_VALID_KEY_SIZE.contains(&key_size) { - return Err(ValidationError::new( - "invalid key size, possible values are 2048/3072/4096", - )); +fn validate_key_size_core( + key_type: &X509KeyType, + key_length: &String, +) -> std::result::Result<(), ValidationError> { + match key_type { + X509KeyType::Rsa | X509KeyType::Dsa => { + if X509_VALID_KEY_SIZE.contains(&key_length.as_str()) { + Ok(()) + } else { + return Err(ValidationError::new( + "invaild key size, RSA/DSA possible values are 2048/3072/4049", + )); + } + } + X509KeyType::Sm2 => { + if X509_SM2_VALID_KEY_SIZE.contains(&key_length.as_str()) { + Ok(()) + } else { + return Err(ValidationError::new( + "invaild key size, SM2 possible values are 256", + )); + } + } } - Ok(()) +} + +fn validate_x509_key_size_for_import( + p: &X509KeyImportParameter, +) -> std::result::Result<(), ValidationError> { + validate_key_size_core(&p.key_type, &p.key_length) +} + +fn validate_x509_key_size_for_generation( + p: &X509KeyGenerationParameter, +) -> std::result::Result<(), ValidationError> { + validate_key_size_core(&p.key_type, &p.key_length) } fn days_in_duration(time: &str) -> Result { @@ -871,7 +893,9 @@ mod test { parameter.insert("key_type".to_string(), "".to_string()); attributes_validate::(¶meter) .expect_err("invalid empty key type"); - for key_type in all::().collect::>() { + + let key_types = vec![X509KeyType::Rsa, X509KeyType::Dsa]; + for key_type in key_types { parameter.insert("key_type".to_string(), key_type.to_string()); attributes_validate::(¶meter).expect("valid key type"); } @@ -945,12 +969,21 @@ mod test { } #[tokio::test] - async fn test_generate_ca_with_possible_digest_hash() { + async fn test_generate_ca_with_international_algo() { let mut parameter = get_default_parameter(); //choose 4 random digest algorithm let dummy_engine = get_encryption_engine(); let infra_config = get_infra_config(); - for hash in all::().collect::>() { + let algos = vec![ + X509DigestAlgorithm::MD5, + X509DigestAlgorithm::SHA1, + X509DigestAlgorithm::SHA2_224, + X509DigestAlgorithm::SHA2_256, + X509DigestAlgorithm::SHA2_384, + X509DigestAlgorithm::SHA2_512, + ]; + + for hash in algos { parameter.insert("digest_algorithm".to_string(), hash.to_string()); let sec_datakey = SecDataKey::load( &get_default_datakey(None, Some(parameter.clone()), Some(KeyType::X509CA)), @@ -965,6 +998,44 @@ mod test { } } + #[tokio::test] + async fn test_generate_ca_with_sm_algo() { + let mut parameter = get_default_parameter(); + let dummy_engine = get_encryption_engine(); + let infra_config = get_infra_config(); + parameter.insert("key_type".to_string(), "sm2".to_string()); + parameter.insert("digest_algorithm".to_string(), "sm3".to_string()); + parameter.insert("key_length".to_string(), "256".to_string()); + let sec_datakey = SecDataKey::load( + &get_default_datakey(None, Some(parameter.clone()), Some(KeyType::X509CA)), + &dummy_engine, + ) + .await + .expect("load sec datakey successfully"); + let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + plugin + .generate_keys(&KeyType::X509CA, &infra_config) + .expect(format!("generate ca key with digest sm3 successfully").as_str()); + } + + #[tokio::test] + async fn test_generate_sm_ca_with_incorrect_length() { + let mut parameter = get_default_parameter(); + let dummy_engine = get_encryption_engine(); + let infra_config = get_infra_config(); + parameter.insert("key_type".to_string(), "sm2".to_string()); + parameter.insert("digest_algorithm".to_string(), "sm3".to_string()); + let sec_datakey = SecDataKey::load( + &get_default_datakey(None, Some(parameter.clone()), Some(KeyType::X509CA)), + &dummy_engine, + ) + .await + .expect("load sec datakey successfully"); + let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let result = plugin.generate_keys(&KeyType::X509CA, &infra_config); + assert!(result.is_err()); + } + #[tokio::test] async fn test_generate_key_with_possible_length() { let mut parameter = get_default_parameter(); @@ -988,11 +1059,14 @@ mod test { } #[tokio::test] - async fn test_generate_key_with_possible_key_type() { + async fn test_generate_key_with_international_type() { let mut parameter = get_default_parameter(); let dummy_engine = get_encryption_engine(); let infra_config = get_infra_config(); - for key_type in all::().collect::>() { + + let types = vec![X509KeyType::Rsa, X509KeyType::Dsa]; + + for key_type in types { parameter.insert("key_type".to_string(), key_type.to_string()); let sec_datakey = SecDataKey::load( &get_default_datakey(None, Some(parameter.clone()), Some(KeyType::X509CA)), diff --git a/src/util/sign.rs b/src/util/sign.rs index 7076736..dc9e351 100644 --- a/src/util/sign.rs +++ b/src/util/sign.rs @@ -42,6 +42,7 @@ pub enum FileType { KernelModule, EfiImage, ImaEvm, + GenericCms, } impl Display for FileType { @@ -52,6 +53,7 @@ impl Display for FileType { FileType::KernelModule => write!(f, "ko"), FileType::EfiImage => write!(f, "efi"), FileType::ImaEvm => write!(f, "ima"), + FileType::GenericCms => write!(f, "GenericCms"), } } } -- Gitee From a2985328d2fd2742e65f127dd0a2c9a36dd5742d Mon Sep 17 00:00:00 2001 From: unknown <1592085657@qq.com> Date: Fri, 5 Dec 2025 16:36:18 +0800 Subject: [PATCH 2/2] support cms with timestamp --- app/src/pages/listShow/ImportX509.vue | 24 +- scripts/initialize-user-and-keys.sh | 6 +- src/application/datakey.rs | 52 ++- src/client/cmd/add.rs | 32 ++ src/client/file_handler/factory.rs | 1 - src/client/file_handler/generic_cms.rs | 141 -------- src/client/file_handler/kernel_module.rs | 19 +- src/client/file_handler/mod.rs | 1 - src/client/file_handler/p7s.rs | 14 + src/domain/datakey/repository.rs | 2 +- src/domain/sign_plugin.rs | 2 +- src/domain/sign_service.rs | 2 + .../database/model/datakey/repository.rs | 18 +- src/infra/sign_backend/memory/backend.rs | 18 +- src/infra/sign_plugin/cms.rs | 224 ++++++++++++- src/infra/sign_plugin/openpgp.rs | 20 +- src/infra/sign_plugin/signers.rs | 5 +- src/infra/sign_plugin/x509.rs | 310 ++++++++++++++++-- src/presentation/handler/data/sign_handler.rs | 2 +- src/util/options.rs | 3 + src/util/sign.rs | 7 +- 21 files changed, 671 insertions(+), 232 deletions(-) delete mode 100644 src/client/file_handler/generic_cms.rs diff --git a/app/src/pages/listShow/ImportX509.vue b/app/src/pages/listShow/ImportX509.vue index 0351f0e..7a516a4 100644 --- a/app/src/pages/listShow/ImportX509.vue +++ b/app/src/pages/listShow/ImportX509.vue @@ -60,26 +60,6 @@ />
- - - - - - - - - - , data: Vec, ) -> Result>; - async fn get_by_type_and_name(&self, key_type: String, key_name: String) -> Result; - + async fn get_by_type_and_name( + &self, + key_type: Option, + key_name: String, + ) -> Result; + async fn get_timestamp_key_by_type_and_name( + &self, + key_type: Option, + key_name: String, + ) -> Result; //method below used for maintenance fn start_key_rotate_loop(&self, cancel_token: CancellationToken) -> Result<()>; @@ -90,6 +99,7 @@ where repository: R, sign_service: Arc>>, container: TimedFixedSizeCache, + timestamp_container: TimedFixedSizeCache, } impl DBKeyService @@ -102,6 +112,7 @@ where repository, sign_service: Arc::new(RwLock::new(sign_service)), container: TimedFixedSizeCache::new(Some(100), None, None, None), + timestamp_container: TimedFixedSizeCache::new(Some(100), None, None, None), } } @@ -538,15 +549,28 @@ where options: &HashMap, data: Vec, ) -> Result> { - let datakey = self.get_by_type_and_name(key_type, key_name).await?; + let mut timekey: Option = None; + if let Some(timestamp_key) = options.get(options::TIMESTAMP_KEY) { + if !timestamp_key.is_empty() { + timekey = Some( + self.get_timestamp_key_by_type_and_name(None, timestamp_key.to_string()) + .await?, + ); + } + } + let datakey = self.get_by_type_and_name(Some(key_type), key_name).await?; self.sign_service .read() .await - .sign(&datakey, data, options.clone()) + .sign(&datakey, timekey, data, options.clone()) .await } - async fn get_by_type_and_name(&self, key_type: String, key_name: String) -> Result { + async fn get_by_type_and_name( + &self, + key_type: Option, + key_name: String, + ) -> Result { if let Some(datakey) = self.container.get_sign_datakey(&key_name).await { return Ok(datakey); } @@ -560,6 +584,24 @@ where Ok(key) } + async fn get_timestamp_key_by_type_and_name( + &self, + key_type: Option, + key_name: String, + ) -> Result { + if let Some(datakey) = self.timestamp_container.get_sign_datakey(&key_name).await { + return Ok(datakey); + } + let key = self + .repository + .get_enabled_key_by_type_and_name_with_parent_key(key_type, key_name.clone()) + .await?; + self.timestamp_container + .update_sign_datakey(&key_name, key.clone()) + .await?; + Ok(key) + } + fn start_key_rotate_loop(&self, cancel_token: CancellationToken) -> Result<()> { let sign_service = self.sign_service.clone(); let mut interval = time::interval(Duration::seconds(60 * 60 * 2).to_std()?); diff --git a/src/client/cmd/add.rs b/src/client/cmd/add.rs index 90e2301..f18163f 100644 --- a/src/client/cmd/add.rs +++ b/src/client/cmd/add.rs @@ -21,6 +21,7 @@ use clap::Args; use config::Config; use regex::Regex; use std::collections::HashMap; +use std::fs; use std::path::PathBuf; use std::sync::{atomic::AtomicBool, Arc, RwLock}; use tokio::runtime; @@ -88,6 +89,14 @@ pub struct CommandAdd { help = "force create rpm v3 signature, default is false. only support when file type is rpm" )] rpm_v3: bool, + #[arg(long, default_value = "")] + #[arg( + help = "specify the path to the CRL file, which is used in CMS signing. only PEM format is supported" + )] + crl: String, + #[arg(long, default_value = "")] + #[arg(help = "Specify the timestamp key name, which is used in CMS signing")] + timestamp_key: String, } #[derive(Clone)] @@ -106,6 +115,8 @@ pub struct CommandAddHandler { sign_type: SignType, token: Option, rpm_v3: bool, + timestamp_key: String, + crl: String, sign_options: Option>, } @@ -120,6 +131,11 @@ impl CommandAddHandler { options::RPM_V3_SIGNATURE.to_string(), self.rpm_v3.to_string(), ), + ( + options::TIMESTAMP_KEY.to_string(), + self.timestamp_key.to_string(), + ), + (options::CRL.to_string(), self.crl.to_string()), ]) } else { self.sign_options.clone().unwrap() @@ -223,6 +239,19 @@ impl SignCommand for CommandAddHandler { token = Some(t); } } + + let mut crl: String = String::new(); + if !command.crl.is_empty() { + crl = match fs::read_to_string(command.crl.clone()) { + Ok(crl) => crl, + Err(e) => { + return Err(error::Error::FileFoundError(format!( + "crl file: {} read failed {}", + command.crl, e + ))); + } + }; + } Ok(CommandAddHandler { worker_threads, buffer_size: config.read()?.get_string("buffer_size")?.parse()?, @@ -238,6 +267,8 @@ impl SignCommand for CommandAddHandler { sign_type: command.sign_type, token, rpm_v3: command.rpm_v3, + timestamp_key: command.timestamp_key, + crl: crl, sign_options: None, }) } @@ -302,6 +333,7 @@ impl SignCommand for CommandAddHandler { Ok(f) => f, Err(err) => return Some(err), }; + // key_attributes(digest_algo) need to be obtained when generate cms signature info!("starting to sign {} files", files.len()); let mut signer = RemoteSigner::new(channel.unwrap(), self.buffer_size, self.token.clone()); diff --git a/src/client/file_handler/factory.rs b/src/client/file_handler/factory.rs index a830079..5f78d4b 100644 --- a/src/client/file_handler/factory.rs +++ b/src/client/file_handler/factory.rs @@ -16,7 +16,6 @@ use super::efi::EfiFileHandler; use super::generic::GenericFileHandler; -use super::generic_cms::CmsFileHandler; use super::ima::ImaFileHandler; use super::kernel_module::KernelModuleFileHandler; use super::p7s::CmsFileHandler; diff --git a/src/client/file_handler/generic_cms.rs b/src/client/file_handler/generic_cms.rs deleted file mode 100644 index d843fb3..0000000 --- a/src/client/file_handler/generic_cms.rs +++ /dev/null @@ -1,141 +0,0 @@ -/* - * - * * // Copyright (c) 2025 Huawei Technologies Co.,Ltd. All rights reserved. - * * // - * * // signatrust is licensed under Mulan PSL v2. - * * // You can use this software according to the terms and conditions of the Mulan - * * // PSL v2. - * * // You may obtain a copy of Mulan PSL v2 at: - * * // http://license.coscl.org.cn/MulanPSL2 - * * // THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY - * * // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO - * * // NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. - * * // See the Mulan PSL v2 for more details. - * - */ - -use super::traits::FileHandler; -use crate::util::error::{Error, Result}; -use crate::util::options; -use crate::util::sign::{KeyType, SignType}; -use async_trait::async_trait; -use std::collections::HashMap; -use std::path::PathBuf; -use tokio::fs; -use uuid::Uuid; - -const CMS_EXTENSION: &str = "p7s"; - -#[derive(Clone)] -pub struct CmsFileHandler {} - -impl CmsFileHandler { - pub fn new() -> Self { - Self {} - } -} - -#[async_trait] -impl FileHandler for CmsFileHandler { - fn validate_options(&self, sign_options: &mut HashMap) -> Result<()> { - if let Some(detached) = sign_options.get(options::DETACHED) { - if detached == "false" { - return Err(Error::InvalidArgumentError( - "generic-cms only support detached signature".to_string(), - )); - } - } - - if let Some(key_type) = sign_options.get(options::KEY_TYPE) { - if key_type != KeyType::X509EE.to_string().as_str() { - return Err(Error::InvalidArgumentError( - "generic-cms only support x509 key".to_string(), - )); - } - } - - if let Some(sign_type) = sign_options.get(options::SIGN_TYPE) { - if sign_type != SignType::Cms.to_string().as_str() { - return Err(Error::InvalidArgumentError( - "generic-cms file only support cms".to_string(), - )); - } - } - Ok(()) - } - - async fn assemble_data( - &self, - path: &PathBuf, - data: Vec>, - temp_dir: &PathBuf, - _sign_options: &HashMap, - _key_attributes: &HashMap, - ) -> Result<(String, String)> { - let temp_file = temp_dir.join(Uuid::new_v4().to_string()); - fs::write(temp_file.clone(), &data[0]).await?; - Ok(( - temp_file.as_path().display().to_string(), - format!("{}.{}", path.as_path().display(), CMS_EXTENSION), - )) - } -} - -#[cfg(test)] -mod test { - use super::*; - use std::env; - - #[test] - fn test_validate_options() { - let mut options = HashMap::new(); - options.insert(options::DETACHED.to_string(), "false".to_string()); - let handler = CmsFileHandler::new(); - let result = handler.validate_options(&mut options); - assert!(result.is_err()); - assert_eq!( - result.unwrap_err().to_string(), - "invalid argument: generic-cms only support detached signature" - ); - - options.insert(options::DETACHED.to_string(), "true".to_string()); - options.insert(options::KEY_TYPE.to_string(), KeyType::Pgp.to_string()); - let result = handler.validate_options(&mut options); - assert!(result.is_err()); - assert_eq!( - result.unwrap_err().to_string(), - "invalid argument: generic-cms only support x509 key" - ); - - options.insert(options::KEY_TYPE.to_string(), KeyType::X509EE.to_string()); - options.insert( - options::SIGN_TYPE.to_string(), - SignType::RsaHash.to_string(), - ); - let result = handler.validate_options(&mut options); - assert!(result.is_err()); - assert_eq!( - result.unwrap_err().to_string(), - "invalid argument: generic-cms file only support cms" - ); - options.insert(options::SIGN_TYPE.to_string(), SignType::Cms.to_string()); - let result = handler.validate_options(&mut options); - assert!(result.is_ok()); - } - - #[tokio::test] - async fn test_assemble_data() { - let handler = CmsFileHandler::new(); - let options = HashMap::new(); - let path = PathBuf::from("./test_data/test.txt"); - let data = vec![vec![1, 2, 3]]; - let temp_dir = env::temp_dir(); - let result = handler - .assemble_data(&path, data, &temp_dir, &options, &HashMap::new()) - .await; - assert!(result.is_ok()); - let (temp_file, file_name) = result.expect("invoke assemble data should work"); - assert_eq!(temp_file.starts_with(temp_dir.to_str().unwrap()), true); - assert_eq!(file_name, "./test_data/test.txt.p7s"); - } -} diff --git a/src/client/file_handler/kernel_module.rs b/src/client/file_handler/kernel_module.rs index 081a155..2efc824 100644 --- a/src/client/file_handler/kernel_module.rs +++ b/src/client/file_handler/kernel_module.rs @@ -167,11 +167,11 @@ impl FileHandler for KernelModuleFileHandler { } if let Some(sign_type) = sign_options.get(options::SIGN_TYPE) { - if sign_type != SignType::Cms.to_string().as_str() + if sign_type != SignType::KernelCms.to_string().as_str() && sign_type != SignType::PKCS7.to_string().as_str() { return Err(Error::InvalidArgumentError( - "kernel module file only support cms or pkcs7 sign type".to_string(), + "kernel module file only support kernel-cms or pkcs7 sign type".to_string(), )); } } @@ -265,7 +265,7 @@ mod test { #[test] fn test_get_raw_content_with_small_unsigned_content() { - let mut sign_options = HashMap::new(); + let sign_options = HashMap::new(); let file_handler = KernelModuleFileHandler::new(); let (name, original_content) = generate_unsigned_kernel_module(SIGNATURE_SIZE - 1) .expect("generate unsigned kernel module failed"); @@ -279,7 +279,7 @@ mod test { #[test] fn test_get_raw_content_with_large_unsigned_content() { - let mut sign_options = HashMap::new(); + let sign_options = HashMap::new(); let file_handler = KernelModuleFileHandler::new(); let (name, original_content) = generate_unsigned_kernel_module(SIGNATURE_SIZE + 100) .expect("generate unsigned kernel module failed"); @@ -293,7 +293,7 @@ mod test { #[test] fn test_get_raw_content_with_signed_content() { - let mut sign_options = HashMap::new(); + let sign_options = HashMap::new(); let file_handler = KernelModuleFileHandler::new(); let (name, original_content) = generate_signed_kernel_module(100, false) .expect("generate signed kernel module failed"); @@ -307,7 +307,7 @@ mod test { #[test] fn test_get_raw_content_with_invalid_signed_content() { - let mut sign_options = HashMap::new(); + let sign_options = HashMap::new(); let file_handler = KernelModuleFileHandler::new(); let (name, _) = generate_signed_kernel_module(100, true).expect("generate signed kernel module failed"); @@ -340,10 +340,13 @@ mod test { assert!(result.is_err()); assert_eq!( result.unwrap_err().to_string(), - "invalid argument: kernel module file only support cms or pkcs7 sign type" + "invalid argument: kernel module file only support kernel-cms or pkcs7 sign type" ); - options.insert(options::SIGN_TYPE.to_string(), SignType::Cms.to_string()); + options.insert( + options::SIGN_TYPE.to_string(), + SignType::KernelCms.to_string(), + ); let result = handler.validate_options(&mut options); assert!(result.is_ok()); diff --git a/src/client/file_handler/mod.rs b/src/client/file_handler/mod.rs index 1529e8f..79c1f77 100644 --- a/src/client/file_handler/mod.rs +++ b/src/client/file_handler/mod.rs @@ -1,7 +1,6 @@ pub mod efi; pub mod factory; pub mod generic; -pub mod generic_cms; pub mod ima; pub mod kernel_module; pub mod p7s; diff --git a/src/client/file_handler/p7s.rs b/src/client/file_handler/p7s.rs index 3391bb9..72a9527 100644 --- a/src/client/file_handler/p7s.rs +++ b/src/client/file_handler/p7s.rs @@ -15,10 +15,12 @@ */ use super::traits::FileHandler; +use crate::util::attributes::PkeyHashAlgo; use crate::util::error::{Error, Result}; use crate::util::options; use crate::util::sign::{KeyType, SignType}; use async_trait::async_trait; +use openssl::hash::hash; use std::collections::HashMap; use std::path::PathBuf; use tokio::fs; @@ -79,6 +81,18 @@ impl FileHandler for CmsFileHandler { format!("{}.{}", path.as_path().display(), CMS_EXTENSION), )) } + + async fn split_data( + &self, + path: &PathBuf, + _sign_options: &mut HashMap, + key_attributes: &HashMap, + ) -> Result>> { + let content = fs::read(path).await?; + let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(key_attributes); + let digest = hash(digest_algo, &content)?; + Ok(vec![digest.to_vec()]) + } } #[cfg(test)] diff --git a/src/domain/datakey/repository.rs b/src/domain/datakey/repository.rs index a2694b0..26b855b 100644 --- a/src/domain/datakey/repository.rs +++ b/src/domain/datakey/repository.rs @@ -41,7 +41,7 @@ pub trait Repository: Send + Sync { async fn update_key_data(&self, data_key: DataKey) -> Result<()>; async fn get_enabled_key_by_type_and_name_with_parent_key( &self, - key_type: String, + key_type: Option, name: String, ) -> Result; async fn request_delete_key( diff --git a/src/domain/sign_plugin.rs b/src/domain/sign_plugin.rs index dd2c901..4b907a7 100644 --- a/src/domain/sign_plugin.rs +++ b/src/domain/sign_plugin.rs @@ -20,7 +20,7 @@ use chrono::{DateTime, Utc}; use std::collections::HashMap; pub trait SignPlugins: Send + Sync { - fn new(db: SecDataKey) -> Result + fn new(db: SecDataKey, timestamp_key: Option) -> Result where Self: Sized; fn validate_and_update(key: &mut DataKey) -> Result<()> diff --git a/src/domain/sign_service.rs b/src/domain/sign_service.rs index ab7ed34..b3a59c7 100644 --- a/src/domain/sign_service.rs +++ b/src/domain/sign_service.rs @@ -49,10 +49,12 @@ pub trait SignBackend: Send + Sync { async fn sign( &self, data_key: &DataKey, + timestamp_key: Option, content: Vec, options: HashMap, ) -> Result>; async fn decode_public_keys(&self, data_key: &mut DataKey) -> Result<()>; + async fn decode_private_keys(&self, data_key: &mut DataKey) -> Result<()>; async fn generate_crl_content( &self, data_key: &DataKey, diff --git a/src/infra/database/model/datakey/repository.rs b/src/infra/database/model/datakey/repository.rs index bf735c0..ce8d3e3 100644 --- a/src/infra/database/model/datakey/repository.rs +++ b/src/infra/database/model/datakey/repository.rs @@ -576,9 +576,16 @@ impl<'a> Repository for DataKeyRepository<'a> { async fn get_enabled_key_by_type_and_name_with_parent_key( &self, - key_type: String, + key_type: Option, name: String, ) -> Result { + let mut cond = Condition::all(); + cond = cond.add(datakey_dto::Column::Name.eq(name)); + if let Some(t) = key_type { + cond = cond.add(datakey_dto::Column::KeyType.eq(t)); + } + cond = cond.add(datakey_dto::Column::KeyState.eq(KeyState::Enabled.to_string())); + match datakey_dto::Entity::find() .select_only() .columns(datakey_dto::Column::iter().filter(|col| { @@ -590,12 +597,7 @@ impl<'a> Repository for DataKeyRepository<'a> { | datakey_dto::Column::X509CrlUpdateAt ) })) - .filter( - Condition::all() - .add(datakey_dto::Column::Name.eq(name)) - .add(datakey_dto::Column::KeyType.eq(key_type)) - .add(datakey_dto::Column::KeyState.eq(KeyState::Enabled.to_string())), - ) + .filter(cond) .one(self.db_connection) .await? { @@ -1452,7 +1454,7 @@ mod tests { assert_eq!( datakey_repository .get_enabled_key_by_type_and_name_with_parent_key( - "openpgp".to_string(), + Some("openpgp".to_string()), "fake_name".to_string() ) .await?, diff --git a/src/infra/sign_backend/memory/backend.rs b/src/infra/sign_backend/memory/backend.rs index 8f914b5..729c14c 100644 --- a/src/infra/sign_backend/memory/backend.rs +++ b/src/infra/sign_backend/memory/backend.rs @@ -106,7 +106,7 @@ impl SignBackend for MemorySignBackend { async fn generate_keys(&self, data_key: &mut DataKey) -> Result<()> { let sec_key = SecDataKey::load(data_key, &self.engine).await?; - let content = Signers::load_from_data_key(&data_key.key_type, sec_key)? + let content = Signers::load_from_data_key(&data_key.key_type, sec_key, None)? .generate_keys(&data_key.key_type, &self.infra_configs)?; data_key.private_key = self.engine.encode(content.private_key).await?; data_key.public_key = self.engine.encode(content.public_key).await?; @@ -123,11 +123,17 @@ impl SignBackend for MemorySignBackend { async fn sign( &self, data_key: &DataKey, + timestamp_key: Option, content: Vec, options: HashMap, ) -> Result> { let sec_key = SecDataKey::load(data_key, &self.engine).await?; - Signers::load_from_data_key(&data_key.key_type, sec_key)?.sign(content, options) + let mut timestamp_sec_key: Option = None; + if let Some(ref key) = timestamp_key { + timestamp_sec_key = Some(SecDataKey::load(&key, &self.engine).await?); + } + Signers::load_from_data_key(&data_key.key_type, sec_key, timestamp_sec_key)? + .sign(content, options) } async fn decode_public_keys(&self, data_key: &mut DataKey) -> Result<()> { @@ -136,6 +142,12 @@ impl SignBackend for MemorySignBackend { Ok(()) } + async fn decode_private_keys(&self, data_key: &mut DataKey) -> Result<()> { + data_key.private_key = self.engine.decode(data_key.private_key.clone()).await?; + data_key.certificate = self.engine.decode(data_key.certificate.clone()).await?; + Ok(()) + } + async fn generate_crl_content( &self, data_key: &DataKey, @@ -144,7 +156,7 @@ impl SignBackend for MemorySignBackend { next_update: DateTime, ) -> Result> { let sec_key = SecDataKey::load(data_key, &self.engine).await?; - Signers::load_from_data_key(&data_key.key_type, sec_key)?.generate_crl_content( + Signers::load_from_data_key(&data_key.key_type, sec_key, None)?.generate_crl_content( revoked_keys, last_update, next_update, diff --git a/src/infra/sign_plugin/cms.rs b/src/infra/sign_plugin/cms.rs index e99db55..06dbe8f 100644 --- a/src/infra/sign_plugin/cms.rs +++ b/src/infra/sign_plugin/cms.rs @@ -3,6 +3,7 @@ use crate::util::attributes::PkeyHashAlgo; use crate::util::error::{Error, Result}; use crate::util::options; use foreign_types_shared::{ForeignType, ForeignTypeRef}; +use openssl::hash::hash; use openssl::pkey; use openssl::x509; use openssl_sys::{ @@ -346,6 +347,10 @@ fn generate_timestamp_req( )); } + let data: &[u8] = std::slice::from_raw_parts(data_ptr, data_len.try_into().unwrap()); + let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(&attributes); + let digest = hash(digest_algo, &data)?; + // step2. generate ts_req from signature let ts_req = TS_REQ_new(); let mut ts_guard = TsGuard(ts_req); @@ -376,7 +381,12 @@ fn generate_timestamp_req( )); } - if TS_MSG_IMPRINT_set_msg(msg_imprint, data_ptr, data_len) != 1 { + if TS_MSG_IMPRINT_set_msg( + msg_imprint, + digest.to_vec().as_ptr(), + digest.len().try_into().unwrap(), + ) != 1 + { return Err(Error::RemoteSignError( "TS_MSG_IMPRINT_set_msg failed".to_string(), )); @@ -779,3 +789,215 @@ impl CmsPlugin { } } } + +#[cfg(test)] +const SM2_CRT: &str = "-----BEGIN CERTIFICATE----- +MIIB1DCCAXoCFGfoVD/6iDpHYUbmTA0+LH/b4tfuMAoGCCqBHM9VAYN1MGwxCzAJ +BgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRIw +EAYDVQQKDAlNeUNvbXBhbnkxDzANBgNVBAsMBlJvb3RDQTEUMBIGA1UEAwwLU00y +IFJvb3QgQ0EwHhcNMjUxMTAzMDgxODEyWhcNMzUxMTAxMDgxODEyWjBsMQswCQYD +VQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzESMBAG +A1UECgwJTXlDb21wYW55MQ8wDQYDVQQLDAZSb290Q0ExFDASBgNVBAMMC1NNMiBS +b290IENBMFowFAYIKoEcz1UBgi0GCCqBHM9VAYItA0IABNYo1OwvitLruiU3oRAc +uaLSplc2Vrj19z2oPicvx8hn3fQLYlqKrKcFvKOWllL3ByQVcMJ4HmRylmOrk24q +4xYwCgYIKoEcz1UBg3UDSAAwRQIhAMnIl0Em/3b8hhR9Ly/FGlt3q2IN1EHLg64+ +JGLqK0DFAiAULqROgRSmSWpJgMzU8KMoPfDM7CJ5/NCnDqI3oM9uTw== +-----END CERTIFICATE-----"; +#[cfg(test)] +const SM2_KEY: &str = "-----BEGIN PRIVATE KEY----- +MIGIAgEAMBQGCCqBHM9VAYItBggqgRzPVQGCLQRtMGsCAQEEIDUaoPl+RCqHV/Un +qWcBnNWXVAOM7BMiiPWQFFotA1h0oUQDQgAE1ijU7C+K0uu6JTehEBy5otKmVzZW +uPX3Pag+Jy/HyGfd9AtiWoqspwW8o5aWUvcHJBVwwngeZHKWY6uTbirjFg== +-----END PRIVATE KEY-----"; +#[cfg(test)] +const RSA_CRT: &str = "-----BEGIN CERTIFICATE----- +MIIDYTCCAkkCFElnH8LftfLwEwPJRQ3i0hF0XQl4MA0GCSqGSIb3DQEBCwUAMG0x +CzAJBgNVBAYTAkNOMQ0wCwYDVQQIDAR0ZXN0MQ0wCwYDVQQHDAR0ZXN0MQ0wCwYD +VQQKDAR0ZXN0MQ0wCwYDVQQLDAR0ZXN0MQ0wCwYDVQQDDAR0ZXN0MRMwEQYJKoZI +hvcNAQkBFgR0ZXN0MB4XDTI1MTEwMzA3MDkyM1oXDTM1MTEwMTA3MDkyM1owbTEL +MAkGA1UEBhMCQ04xDTALBgNVBAgMBHRlc3QxDTALBgNVBAcMBHRlc3QxDTALBgNV +BAoMBHRlc3QxDTALBgNVBAsMBHRlc3QxDTALBgNVBAMMBHRlc3QxEzARBgkqhkiG +9w0BCQEWBHRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRFYHs +vLSybVatQSyKjNFkoiKpYvy8eD2SwJtUhX1aXgNi2JVV8YKf7ok0NxXQKFovhlP0 +rB7NOFbPw9DYceS9UG6M/PgyhhxAAP/oY8Sr7+Kyz1uf85Q/gGM0wzA27OdgBhGe +BLv6Xg5MDSgXGAScnHTgTP4Ibt9b6xqV3jlw4dPYmaVnD24hBj1afsimgCEN2Iic +vE3cxFBd6zxTSQdU7tcrPlspAS1rmL5E0opIavE8RTOLnjlcFZH1Hcyl89V1u0vn +pRFllrTOtIKX7Px1yTlNXd6G1iaQgwzdLh8N1NxTgeFRwTOjW9cjeGbsfQwERdY6 +4bBhaTQVBraR0FqjAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAGNwWCzbVFD1bgST +lGXMSiCqi9H1UaQxJ4goq742qtoyCKkDEY+7289GmFPWufbK366kZBj52ibEMm7w +4Tp9jVnlf9amkSC15JdBLeHuDT8QRNWMwj+PgVHhcBt+9thwYTiM/mQoxCBJeFiS +vPDWEQxY9/OAh7lkZb+ZWrBxdz7wMa4UiBcfzpmT15vYkG0CvoLQPe73PDIszeF7 +utx8jfSPlUWhLtZ72qcwNeh5QNPr4dclAPIf+mxkipS0QiuWLMmBOLg0AXIi7YeG +2E8IAhEIhpya/SkDBvGiju/8bp5r9x9OdOe61NGuqj86IqLoNWA3/bd03klZq1RA +WD1O4V0= +-----END CERTIFICATE-----"; +#[cfg(test)] +const RSA_KEY: &str = "-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDRFYHsvLSybVat +QSyKjNFkoiKpYvy8eD2SwJtUhX1aXgNi2JVV8YKf7ok0NxXQKFovhlP0rB7NOFbP +w9DYceS9UG6M/PgyhhxAAP/oY8Sr7+Kyz1uf85Q/gGM0wzA27OdgBhGeBLv6Xg5M +DSgXGAScnHTgTP4Ibt9b6xqV3jlw4dPYmaVnD24hBj1afsimgCEN2IicvE3cxFBd +6zxTSQdU7tcrPlspAS1rmL5E0opIavE8RTOLnjlcFZH1Hcyl89V1u0vnpRFllrTO +tIKX7Px1yTlNXd6G1iaQgwzdLh8N1NxTgeFRwTOjW9cjeGbsfQwERdY64bBhaTQV +BraR0FqjAgMBAAECggEAHm3CDAlF8Pu+/sBP8lVeegYArmSEwgxrxZ+jW4XWlvVn +X8DZMaSlgo9tchHG6mryU8f5uvo7vK6DhSw13keWZxjoEJEWXAqu3NUTE/1FF6gg +oYOnuDEeX67YgUrGNGV+TOLFOJlEHu1bqPoh3gPgU/Hkg+pLRECX4CGTVmv83G5g +c/tPT0HMOdXeKpJTzmepLN1QFSnrRbIT1D5uzx4VurVgM+eKXcWN8y2hfZkyeaG7 +ZBW4efepN8gJRysVVAmNQGorMfp6gs26WRS8Se4OyiUthlRy47y1RDNO1mZl8jb/ +im2X5BNiKocnLd08/oFozufrax7RqVoDnFhk0XhYTQKBgQDZOGYjT2IklXW2XAN+ +9FMRk1LDPAJ4S8H1ITTO8K3AqgQJrE+4TJQSNuecq/wOHi1mTChlIdPkoMIWJHEa +vdQgKE4R/2UxLbnpS2uIwtk7UbOFBBicR3Fn/1eB4eFBr97Wv/Pw/8KngGkRANmL +aYnCEsssBvr0IL3Ri+um9C38pQKBgQD2aUGUBAJHMpFmS4gl0mqyjT1X6NoRIHnA +SZNZjsMREqBATrF8fj6+Cm8TptbL0rwheSOc+hugNW4XBOjt9qppe8TVxTj10lq1 +Yi34DCdJ1qoPd7Nn/UKsv1jd0tvW0J7dKBcsq8lHzG7TOVa38iFzVO9WD62doKV0 +RHl3ziFvpwKBgH3z/v2AfUb7RwsbpYdKwpQRWc8ND92TB/9MZuOLmSR7MOYu/Pa/ +qKg7H+evrfK9utNzW4TwrX4HXSMbtF2uLr8Kv+Idth5jBkbpTYw6d123DSIW8vJD +VtXXsHUGdefxw4PAQAHBO6yGf+W1GW+GHbPj091OmttN1OMZf+YJ9lRlAoGAOey4 +W8Etf+slPvTWhn2WU27cUsQMLyaBOHCTUOQ8etD0Funo0ykiOq5dOjNoHvXk/8Fo +W8h3ogutW3/t+bKYkL9loBMCttbCOA1iXQMOYU8zHvu2kuV4PP+mNk8RGshj7/0y +pW+km1o1WzYJaqhisKfwszxwRbOz8Ub/fuhX99UCgYEAjNSTN7iShFW5jQM8L5Pm +BlBogxETPW4TtTgI1xyFHfAY3ZYj+HO9vXLAmuwO6acgDzCSm7h+bfqOLSNPP5Ii +MnUQOZ522LfVOBzi42Hm3aobR4jex/X+3O+NJTi+UtTtfSNBeagkTdh6xORV8XOz +yQO9OpVlZivvOX7n6gjX1jM= +-----END PRIVATE KEY-----"; + +#[test] +fn test_generate_cms_with_rsa() { + let cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate"); + let pkey = + pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key"); + let content = b"test content"; + + let mut attributes = HashMap::new(); + attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string()); + let options = HashMap::new(); + + let result = generate_cms_with_hash(&cert, &pkey, content, options, attributes); + assert!(result.is_ok(), "CMS generation failed: {:?}", result.err()); +} +#[test] +fn test_generate_cms_with_sm2_cert() { + let cert = x509::X509::from_pem(SM2_CRT.as_bytes()).expect("Failed to load certificate"); + let pkey = + pkey::PKey::private_key_from_pem(SM2_KEY.as_bytes()).expect("Failed to load private key"); + let content = b"test content"; + + let mut attributes = HashMap::new(); + attributes.insert(attributes::DIGEST_ALGO.to_string(), "sm3".to_string()); + let options = HashMap::new(); + + let result = generate_cms_with_hash(&cert, &pkey, content, options, attributes); + assert!(result.is_ok(), "CMS generation failed: {:?}", result.err()); +} +#[test] +fn test_generate_cms_with_incorret_alg() { + let cert = x509::X509::from_pem(SM2_CRT.as_bytes()).expect("Failed to load certificate"); + let pkey = + pkey::PKey::private_key_from_pem(SM2_KEY.as_bytes()).expect("Failed to load private key"); + let content = b"test content"; + + let mut attributes = HashMap::new(); + attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string()); + let options = HashMap::new(); + + let result = generate_cms_with_hash(&cert, &pkey, content, options, attributes); + assert!( + result.is_err(), + "Expected error due to use incorret digest algorithm" + ); +} +#[test] +fn test_generate_timestamp_req() { + let cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate"); + let pkey = + pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key"); + let content = b"test content"; + + let mut attributes = HashMap::new(); + attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string()); + let options = HashMap::new(); + + let cms = generate_cms_with_hash(&cert, &pkey, content, options, attributes.clone()) + .expect("CMS generation failed"); + let ts_req = generate_timestamp_req(cms, attributes.clone()); + assert!( + ts_req.is_ok(), + "Timestamp request generation failed: {:?}", + ts_req.err() + ); +} + +#[test] +fn test_generate_timestamp_tst() { + let cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate"); + let pkey = + pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key"); + let content = b"test content"; + + let mut attributes = HashMap::new(); + attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string()); + let options = HashMap::new(); + + let cms = generate_cms_with_hash(&cert, &pkey, content, options, attributes.clone()) + .expect("CMS generation failed"); + let ts_req = + generate_timestamp_req(cms, attributes.clone()).expect("Failed to generate TS request"); + let tst_info = generate_timestamp_tst(ts_req, &cert).expect("TST info generation failed"); + assert!(!tst_info.is_null(), "TST info is null"); +} + +#[test] +fn test_generate_timestamp_signature() { + let cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate"); + let pkey = + pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key"); + let content = b"test content"; + + let mut attributes = HashMap::new(); + attributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string()); + let options = HashMap::new(); + + let cms = generate_cms_with_hash(&cert, &pkey, content, options, attributes.clone()) + .expect("CMS generation failed"); + let ts_req = + generate_timestamp_req(cms, attributes.clone()).expect("Failed to generate TS request"); + let tst_info = generate_timestamp_tst(ts_req, &cert).expect("TST info generation failed"); + + let tsa_cert = x509::X509::from_pem(SM2_CRT.as_bytes()).expect("Failed to load certificate"); + let tsa_pkey = + pkey::PKey::private_key_from_pem(SM2_KEY.as_bytes()).expect("Failed to load private key"); + let mut tsa_pattributes = HashMap::new(); + tsa_pattributes.insert(attributes::DIGEST_ALGO.to_string(), "sm3".to_string()); + + let tst = generate_timestamp_signature(&tsa_cert, &tsa_pkey, tst_info, tsa_pattributes) + .expect("TST generation failed"); + assert!(!tst.is_null(), "TST is null"); +} + +#[test] +fn test_attach_timestamp_to_cms() { + let cert = x509::X509::from_pem(SM2_CRT.as_bytes()).expect("Failed to load certificate"); + let pkey = + pkey::PKey::private_key_from_pem(SM2_KEY.as_bytes()).expect("Failed to load private key"); + let content = b"test content"; + + let mut attributes = HashMap::new(); + attributes.insert(attributes::DIGEST_ALGO.to_string(), "sm3".to_string()); + let options = HashMap::new(); + + let cms = generate_cms_with_hash(&cert, &pkey, content, options, attributes.clone()) + .expect("CMS generation failed"); + let ts_req = + generate_timestamp_req(cms, attributes.clone()).expect("Failed to generate TS request"); + let tst_info = generate_timestamp_tst(ts_req, &cert).expect("TST info generation failed"); + + let tsa_cert = x509::X509::from_pem(RSA_CRT.as_bytes()).expect("Failed to load certificate"); + let tsa_pkey = + pkey::PKey::private_key_from_pem(RSA_KEY.as_bytes()).expect("Failed to load private key"); + let mut tsa_pattributes = HashMap::new(); + tsa_pattributes.insert(attributes::DIGEST_ALGO.to_string(), "sha256".to_string()); + + let tst = generate_timestamp_signature(&tsa_cert, &tsa_pkey, tst_info, tsa_pattributes) + .expect("TST generation failed"); + attach_timestamp_to_cms(cms, tst).expect("CMS Attach Timestamp failed"); +} diff --git a/src/infra/sign_plugin/openpgp.rs b/src/infra/sign_plugin/openpgp.rs index 4421c54..7674a36 100644 --- a/src/infra/sign_plugin/openpgp.rs +++ b/src/infra/sign_plugin/openpgp.rs @@ -134,7 +134,7 @@ impl OpenPGPPlugin { } impl SignPlugins for OpenPGPPlugin { - fn new(db: SecDataKey) -> Result { + fn new(db: SecDataKey, _timestamp_key: Option) -> Result { let mut secret_key = None; let mut public_key = None; if !db.private_key.unsecure().is_empty() { @@ -466,7 +466,7 @@ mod test { .await .expect("load sec datakey successfully"); let plugin = - OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully"); + OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully"); plugin .generate_keys(&KeyType::OpenPGP, &HashMap::new()) .expect( @@ -488,7 +488,7 @@ mod test { .await .expect("load sec datakey successfully"); let plugin = - OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully"); + OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully"); plugin .generate_keys(&KeyType::OpenPGP, &HashMap::new()) .expect(format!("generate key with key size {} successfully", key_size).as_str()); @@ -508,7 +508,7 @@ mod test { .await .expect("load sec datakey successfully"); let plugin = - OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully"); + OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully"); plugin .generate_keys(&KeyType::OpenPGP, &HashMap::new()) .expect(format!("generate key with key type {} successfully", key_type).as_str()); @@ -525,7 +525,8 @@ mod test { ) .await .expect("load sec datakey successfully"); - let plugin = OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully"); + let plugin = + OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully"); plugin .generate_keys(&KeyType::OpenPGP, &HashMap::new()) .expect(format!("generate key with no passphrase successfully").as_str()); @@ -536,7 +537,8 @@ mod test { ) .await .expect("load sec datakey successfully"); - let plugin = OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully"); + let plugin = + OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully"); plugin .generate_keys(&KeyType::OpenPGP, &HashMap::new()) .expect(format!("generate key with passphrase successfully").as_str()); @@ -633,7 +635,8 @@ j0AZp6WE ) .await .expect("load sec datakey successfully"); - let plugin = OpenPGPPlugin::new(sec_datakey).expect("create openpgp plugin successfully"); + let plugin = + OpenPGPPlugin::new(sec_datakey, None).expect("create openpgp plugin successfully"); let keys = plugin .generate_keys(&KeyType::OpenPGP, &HashMap::new()) .expect(format!("generate key successfully").as_str()); @@ -646,7 +649,8 @@ j0AZp6WE attributes: Default::default(), parent: None, }; - let instance = OpenPGPPlugin::new(sec_keys).expect("create openpgp instance successfully"); + let instance = + OpenPGPPlugin::new(sec_keys, None).expect("create openpgp instance successfully"); let signature = instance .sign(content.to_vec(), parameter) .expect("sign successfully"); diff --git a/src/infra/sign_plugin/signers.rs b/src/infra/sign_plugin/signers.rs index fe791f2..1de616a 100644 --- a/src/infra/sign_plugin/signers.rs +++ b/src/infra/sign_plugin/signers.rs @@ -29,11 +29,12 @@ impl Signers { pub fn load_from_data_key( key_type: &KeyType, data_key: SecDataKey, + timestamp_key: Option, ) -> Result> { match key_type { - KeyType::OpenPGP => Ok(Box::new(OpenPGPPlugin::new(data_key)?)), + KeyType::OpenPGP => Ok(Box::new(OpenPGPPlugin::new(data_key, None)?)), KeyType::X509CA | KeyType::X509ICA | KeyType::X509EE => { - Ok(Box::new(X509Plugin::new(data_key)?)) + Ok(Box::new(X509Plugin::new(data_key, timestamp_key)?)) } } } diff --git a/src/infra/sign_plugin/x509.rs b/src/infra/sign_plugin/x509.rs index 698ae1a..5ee7e64 100644 --- a/src/infra/sign_plugin/x509.rs +++ b/src/infra/sign_plugin/x509.rs @@ -22,6 +22,7 @@ use openssl::cms::{CMSOptions, CmsContentInfo}; use openssl::hash::MessageDigest; use openssl::nid::Nid; use openssl::pkcs7::{Pkcs7, Pkcs7Flags}; +use openssl::pkey; use openssl::pkey::PKey; use openssl::stack::Stack; use openssl::x509; @@ -37,6 +38,7 @@ use openssl_sys::{ use secstr::SecVec; use serde::Deserialize; use std::collections::HashMap; +use std::ffi::CString; use std::str::FromStr; use std::time::{Duration, SystemTime}; @@ -49,6 +51,7 @@ use crate::domain::datakey::plugins::x509::{ X509DigestAlgorithm, X509EEUsage, X509KeyType, X509_SM2_VALID_KEY_SIZE, X509_VALID_KEY_SIZE, }; use crate::domain::sign_plugin::SignPlugins; +use crate::infra::sign_plugin::cms::{CmsContext, CmsPlugin, EVP_PKEY_is_a, Step}; use crate::util::attributes; use crate::util::error::{Error, Result}; use crate::util::key::{decode_hex_string_to_u8, encode_u8_to_hex_string}; @@ -178,8 +181,11 @@ pub struct X509Plugin { private_key: SecVec, public_key: SecVec, certificate: SecVec, + tsa_cert: SecVec, + tsa_key: SecVec, identity: String, attributes: HashMap, + tsa_attributes: HashMap, parent_key: Option, } @@ -512,10 +518,32 @@ impl X509Plugin { serial_number: Some(encode_u8_to_hex_string(&serial_number.to_vec())), }) } + + fn detect_key_type(private_key: &PKey) -> Result { + unsafe { + let rsa = CString::new("RSA").unwrap(); + let rsa_pss = CString::new("RSA-PSS").unwrap(); + let dsa = CString::new("DSA").unwrap(); + let sm2 = CString::new("SM2").unwrap(); + if EVP_PKEY_is_a(private_key.as_ptr(), rsa.as_ptr()) == 1 { + Ok(X509KeyType::Rsa.as_str().to_string()) + } else if EVP_PKEY_is_a(private_key.as_ptr(), dsa.as_ptr()) == 1 { + Ok(X509KeyType::Dsa.as_str().to_string()) + } else if EVP_PKEY_is_a(private_key.as_ptr(), sm2.as_ptr()) == 1 { + Ok(X509KeyType::Sm2.as_str().to_string()) + } else if EVP_PKEY_is_a(private_key.as_ptr(), rsa_pss.as_ptr()) == 1 { + Ok(X509KeyType::Rsa.as_str().to_string()) + } else { + Err(Error::InvalidArgumentError( + "key type only support RSA,DSA,SM2".to_string(), + )) + } + } + } } impl SignPlugins for X509Plugin { - fn new(db: SecDataKey) -> Result { + fn new(db: SecDataKey, timestamp_key: Option) -> Result { let mut plugin = Self { name: db.name.clone(), private_key: db.private_key.clone(), @@ -524,10 +552,19 @@ impl SignPlugins for X509Plugin { identity: db.identity.clone(), attributes: db.attributes, parent_key: None, + tsa_cert: SecVec::new(Vec::new()), + tsa_key: SecVec::new(Vec::new()), + tsa_attributes: HashMap::new(), }; if let Some(parent) = db.parent { plugin.parent_key = Some(parent); } + + if let Some(ref key) = timestamp_key { + plugin.tsa_cert = key.certificate.clone(); + plugin.tsa_key = key.private_key.clone(); + plugin.tsa_attributes = key.attributes.clone(); + } Ok(plugin) } @@ -536,11 +573,25 @@ impl SignPlugins for X509Plugin { Self: Sized, { let _ = attributes_validate::(&key.attributes)?; - let _private_key = PKey::private_key_from_pem(&key.private_key)?; + let private_key = PKey::private_key_from_pem(&key.private_key)?; let certificate = x509::X509::from_pem(&key.certificate)?; if !key.public_key.is_empty() { let _public_key = PKey::public_key_from_pem(&key.public_key)?; } + match X509Plugin::detect_key_type(&private_key) { + Ok(key_type) => { + key.attributes + .insert("key_type".to_string(), key_type.to_string()); + } + Err(_e) => { + return Err(Error::InvalidArgumentError( + "key type only support RSA,DSA,SM2".to_string(), + )); + } + } + let bit = private_key.bits(); + key.attributes + .insert("key_length".to_string(), bit.to_string()); let unix_time = Asn1Time::from_unix(0)?.diff(certificate.not_after())?; let expire = SystemTime::UNIX_EPOCH + Duration::from_secs(unix_time.days as u64 * 86400 + unix_time.secs as u64); @@ -644,7 +695,7 @@ impl SignPlugins for X509Plugin { )?; Ok(pkcs7.to_der()?) } - SignType::Cms => { + SignType::KernelCms => { //cms option reference: https://man.openbsd.org/CMS_sign.3 let cms_signature = CmsContentInfo::sign( Some(&certificate), @@ -659,6 +710,39 @@ impl SignPlugins for X509Plugin { )?; Ok(cms_signature.to_der()?) } + SignType::Cms => { + let tsa_cert_pem = self.tsa_cert.unsecure(); + let tsa_key_pem = self.tsa_key.unsecure(); + let mut ctx = CmsContext::new( + &certificate, + &private_key, + content.as_slice(), + &options, + &self.attributes, + &self.tsa_attributes, + tsa_cert_pem, + tsa_key_pem, + ); + + let need_ts = !tsa_cert_pem.is_empty() && !tsa_key_pem.is_empty(); + if need_ts { + info!("tsa cert & key present, timestamp will be attached"); + let steps: &[Step] = &[ + &CmsPlugin::step_generate_cms, + &CmsPlugin::step_generate_ts_req, + &CmsPlugin::step_load_tsa_cert_key, + &CmsPlugin::step_generate_tst_info, + &CmsPlugin::step_generate_timestamp_token, + &CmsPlugin::step_attach_timestamp, + ]; + CmsPlugin::run_steps(&mut ctx, steps)?; + } else { + info!("tsa cert or key is empty, skip timestamp"); + let steps: &[Step] = &[&CmsPlugin::step_generate_cms]; + CmsPlugin::run_steps(&mut ctx, steps)?; + } + CmsPlugin::cms_to_vec(ctx.cms) + } SignType::RsaHash => { // rust-openssl/openssl/src/pkey_ctx.rs let mut signature = vec![]; @@ -825,7 +909,7 @@ mod test { let sec_datakey = SecDataKey::load(&ca_key, &dummy_engine) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); let ca_content = plugin .generate_keys(&KeyType::X509CA, &infra_config) .expect(format!("generate ca key with no passphrase successfully").as_str()); @@ -846,7 +930,7 @@ mod test { let sec_datakey = SecDataKey::load(&ica_key, &dummy_engine) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); let ica_content = plugin .generate_keys(&KeyType::X509ICA, &infra_config) .expect(format!("generate ica key with no passphrase successfully").as_str()); @@ -867,7 +951,7 @@ mod test { let sec_datakey = SecDataKey::load(&ica_key, &dummy_engine) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); let ee_content = plugin .generate_keys(&KeyType::X509EE, &infra_config) .expect(format!("generate ee key with no passphrase successfully").as_str()); @@ -878,10 +962,20 @@ mod test { public_key: SecVec::new(ee_content.public_key.clone()), certificate: SecVec::new(ee_content.certificate.clone()), identity: "".to_string(), - attributes: Default::default(), + attributes: parameter.clone(), parent: None, }; - X509Plugin::new(sec_keys).expect("create x509 instance successfully") + + let timestamp_key = SecDataKey { + name: "".to_string(), + private_key: SecVec::new(ee_content.private_key.clone()), + public_key: SecVec::new(ee_content.public_key.clone()), + certificate: SecVec::new(ee_content.certificate.clone()), + identity: "".to_string(), + attributes: parameter.clone(), + parent: None, + }; + X509Plugin::new(sec_keys, Some(timestamp_key)).expect("create x509 instance successfully") } #[test] @@ -991,7 +1085,7 @@ mod test { ) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); plugin .generate_keys(&KeyType::X509CA, &infra_config) .expect(format!("generate ca key with digest {} successfully", hash).as_str()); @@ -1012,7 +1106,7 @@ mod test { ) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); plugin .generate_keys(&KeyType::X509CA, &infra_config) .expect(format!("generate ca key with digest sm3 successfully").as_str()); @@ -1031,7 +1125,7 @@ mod test { ) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); let result = plugin.generate_keys(&KeyType::X509CA, &infra_config); assert!(result.is_err()); } @@ -1049,7 +1143,7 @@ mod test { ) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); plugin .generate_keys(&KeyType::X509CA, &infra_config) .expect( @@ -1065,10 +1159,6 @@ mod test { let infra_config = get_infra_config(); let types = vec![X509KeyType::Rsa, X509KeyType::Dsa]; -<<<<<<< HEAD - -======= ->>>>>>> d27cb96ece4feb4755163fae3fe7367b451749b8 for key_type in types { parameter.insert("key_type".to_string(), key_type.to_string()); let sec_datakey = SecDataKey::load( @@ -1077,7 +1167,7 @@ mod test { ) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); plugin .generate_keys(&KeyType::X509CA, &infra_config) .expect( @@ -1098,7 +1188,7 @@ mod test { ) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); plugin .generate_keys(&KeyType::X509CA, &infra_config) .expect(format!("generate ca key with no passphrase successfully").as_str()); @@ -1110,7 +1200,7 @@ mod test { ) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); plugin .generate_keys(&KeyType::X509CA, &infra_config) .expect(format!("generate ca key with passphrase successfully").as_str()); @@ -1186,9 +1276,185 @@ X5BboR/QJakEK+H+EUQAiDs= ); } + #[test] + fn test_validate_and_update_with_sm2cert() { + let certificate = "-----BEGIN CERTIFICATE----- +MIIB1DCCAXoCFGfoVD/6iDpHYUbmTA0+LH/b4tfuMAoGCCqBHM9VAYN1MGwxCzAJ +BgNVBAYTAkNOMRAwDgYDVQQIDAdCZWlqaW5nMRAwDgYDVQQHDAdCZWlqaW5nMRIw +EAYDVQQKDAlNeUNvbXBhbnkxDzANBgNVBAsMBlJvb3RDQTEUMBIGA1UEAwwLU00y +IFJvb3QgQ0EwHhcNMjUxMTAzMDgxODEyWhcNMzUxMTAxMDgxODEyWjBsMQswCQYD +VQQGEwJDTjEQMA4GA1UECAwHQmVpamluZzEQMA4GA1UEBwwHQmVpamluZzESMBAG +A1UECgwJTXlDb21wYW55MQ8wDQYDVQQLDAZSb290Q0ExFDASBgNVBAMMC1NNMiBS +b290IENBMFowFAYIKoEcz1UBgi0GCCqBHM9VAYItA0IABNYo1OwvitLruiU3oRAc +uaLSplc2Vrj19z2oPicvx8hn3fQLYlqKrKcFvKOWllL3ByQVcMJ4HmRylmOrk24q +4xYwCgYIKoEcz1UBg3UDSAAwRQIhAMnIl0Em/3b8hhR9Ly/FGlt3q2IN1EHLg64+ +JGLqK0DFAiAULqROgRSmSWpJgMzU8KMoPfDM7CJ5/NCnDqI3oM9uTw== +-----END CERTIFICATE-----"; + let private_key = "-----BEGIN PRIVATE KEY----- +MIGIAgEAMBQGCCqBHM9VAYItBggqgRzPVQGCLQRtMGsCAQEEIDUaoPl+RCqHV/Un +qWcBnNWXVAOM7BMiiPWQFFotA1h0oUQDQgAE1ijU7C+K0uu6JTehEBy5otKmVzZW +uPX3Pag+Jy/HyGfd9AtiWoqspwW8o5aWUvcHJBVwwngeZHKWY6uTbirjFg== +-----END PRIVATE KEY-----"; + let mut datakey = get_default_datakey(None, None, None); + datakey.certificate = certificate.as_bytes().to_vec(); + datakey.private_key = private_key.as_bytes().to_vec(); + let _ = X509Plugin::validate_and_update(&mut datakey); + if let Some(value) = datakey.attributes.get("key_length") { + assert_eq!(value, "256"); + } else { + panic!("Expected key 'key_length' not found in the map."); + } + + if let Some(value) = datakey.attributes.get("key_type") { + assert_eq!(value, "sm2"); + } else { + panic!("Expected key 'key_type' not found in the map."); + } + } + + #[test] + fn test_validate_and_update_with_rsacert() { + let certificate = "-----BEGIN CERTIFICATE----- +MIIDSzCCAjOgAwIBAgIUKTs/prIakrwRyyYbYcfoy6YC6rkwDQYJKoZIhvcNAQEL +BQAwTjELMAkGA1UEBhMCQ04xCzAJBgNVBAgMAmNuMQswCQYDVQQHDAJjbjELMAkG +A1UECgwCY24xCzAJBgNVBAsMAmNuMQswCQYDVQQDDAJDTjAeFw0yNTExMTgxMjAy +MTBaFw0yNjExMTgxMjAyMTBaME4xCzAJBgNVBAYTAkNOMQswCQYDVQQIDAJjbjEL +MAkGA1UEBwwCY24xCzAJBgNVBAoMAmNuMQswCQYDVQQLDAJjbjELMAkGA1UEAwwC +Q04wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCf87TFBu9fPQ31mjWA +OcYMQeMmHsI/v1Sng4aBoLhWAWZZ+8AuV37wjBVuYGR4GkZHmIJBmi2YNBkPt/4A +IqX4S8hYAAZUurXcVKub2aQOnfh9OLTIxT/GR56imlcRZDrQ+R/VrpVj3yN3qQHP +M1oyuzWin8osCSDRHfEAWcmKVmvHfq6tYVbnmOSQfiPI8+zxMsaMT07RDogOfxEO +/QkPyj16ih9zpK9N2rqbj7q00JIiTSBZ3P/zlshjiZSGZeVaj3bC4KnzKrdlxS46 +nfJ7tZ+sHxBqSuScO8X/ButvL7HMnB92Ut47C2SnvD/Or+z86G9KDD2pRxqXcfBo +4cYXAgMBAAGjITAfMB0GA1UdDgQWBBSwe9s1aUMAib5BAffASj6l7+5D1DANBgkq +hkiG9w0BAQsFAAOCAQEAPKEWCfyNxRdaemcYEXgp6/AlmFoFaNBLi0ewyKOTuy+N +zQvvgdvFbkdsg9BygNQeZQ/WFpNwrODxMZgcGFLpfxPgq1JrIVrU4CQLn8AgTvkc +2sw/1u4xw4ufyKIYxQdsaOPLXOwedUtY96X0e622oIrr7tmUIse8502WnhllRtHL +aGhroMLSVZrNoAU7KHbC3fnHmQPl9HWx9m37u3DGVtLf8/uXsX74RyV6U9ESHbKw +ptZaepLoHk4fIixgMEVMAlHrZPdtUs7X0B520fi2/BwdChqfewf6thkBK9pzGcss +Z8qL7OaQxFqgD2qi7jjWqkqmYe/yxyIowvu1bWRtbA== +-----END CERTIFICATE-----"; + let private_key = "-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCf87TFBu9fPQ31 +mjWAOcYMQeMmHsI/v1Sng4aBoLhWAWZZ+8AuV37wjBVuYGR4GkZHmIJBmi2YNBkP +t/4AIqX4S8hYAAZUurXcVKub2aQOnfh9OLTIxT/GR56imlcRZDrQ+R/VrpVj3yN3 +qQHPM1oyuzWin8osCSDRHfEAWcmKVmvHfq6tYVbnmOSQfiPI8+zxMsaMT07RDogO +fxEO/QkPyj16ih9zpK9N2rqbj7q00JIiTSBZ3P/zlshjiZSGZeVaj3bC4KnzKrdl +xS46nfJ7tZ+sHxBqSuScO8X/ButvL7HMnB92Ut47C2SnvD/Or+z86G9KDD2pRxqX +cfBo4cYXAgMBAAECggEABZvMCOSXXCWN6cDAg4CDG0bsKhgGA6o306/e9YinLgza +g+k58eYLg2/GCJrEqxlwwW3tk1NOqfmZr11qQKL2YuB1Y/CMSEhLvDAT3GEjSYfs +gKeOX0PbWp6ER3tV9jwne9Bgd2OpxVi7q6R3dcZ9MS4zUUJ9GlIvnmWIX9TGJl2X +NI8tY2SG6J+ow+OOX98IUzLcyVaZR/AJBKUgFl0PjExLfWBDSLJqJJYEamDy1INh +1PLmPjx3Hz+6r5iaVw3OqPJcZzo+9Bj16RhybOlRvrXMAUbD7w2RzfXI096b4De+ +HgyYHFNxj5KpC6qnihkGZVBcbeIjXXPgwexfDipbQQKBgQDNei3y3l0kOXgUgfhp +GMxnmyiHKZmtHzXJJnlNHib7wVPh6YiGDnxVl50MekxSqo53ASv0cpamcDy5TM6N +R5ZeBJW9//MqA0OGbNOCDjYBg2E8jGvyXVPp76ouRxeO5aDordJof4isp6VAPImv +djx3CxxNJKJhzKo5oSJZ9iD+RwKBgQDHR+zsifUDimqg33mrJ92HUMiFxM+haYMV +RySnQZXlDPnVsuZYAdJBUKAMG8ObHy2R2KMnxDdFRRhW6AVzkMkZaQXTUh4M7sx0 +QKWHCXRYWNqoS7qOzxS9O6dW6+kmv/bMCjX6l+z2pIe3bpKuHgOPFVyJuNhz285R +RtbxSrbRsQKBgHLRPA3DfY55YoUrHzEy/z1BsULd1xarIvX0vsF+ANCa9hF92qD2 +RTnaz5IiYLWswpDzIamlwlLc0sHEjoLZpseAjmAuPqWST1A1TXcWE82CqXoZCVTU +G8jT+GeFqD9cRy7dun5UDX5U631alqFqU1094yGkP+ygXdp4FObqJwOPAoGACWMC +7vVknCEV+rPsGDrNfYU5nMtzeEfvC76JJHO7asmcrws5PGYBkGAK2eco5JKoY9lP +fh0I+XNSvS06rIHiZxcCVjzk+3j4GnW9FkpEt7CfxBOlGvr4IB3COR7toYyjRGMq +vb4QRGHlnqdPs3HoewHnlPknAPYWls9+amk5iVECgYEAu90xfsQzVSiQc4FiDC4+ +VU0SuEtd8KmguwckE0RProzJXTs4BhooUB4uwgKA4+IP6cPG9my4vaBL055fHiYb +xpmKp7lj+xPgtXIekR+vzIma2nXiD4Adrs2ITwcjY7dtKLyoLiVJqXQRxWeBoaDS +hlaQghVl9wUq5TwOJgwJDsQ= +-----END PRIVATE KEY-----"; + let mut datakey = get_default_datakey(None, None, None); + datakey.certificate = certificate.as_bytes().to_vec(); + datakey.private_key = private_key.as_bytes().to_vec(); + let _ = X509Plugin::validate_and_update(&mut datakey); + if let Some(value) = datakey.attributes.get("key_length") { + assert_eq!(value, "2048"); + } else { + panic!("Expected key 'key_length' not found in the map."); + } + + if let Some(value) = datakey.attributes.get("key_type") { + assert_eq!(value, "rsa"); + } else { + panic!("Expected key 'key_type' not found in the map."); + } + } + + #[test] + fn test_validate_and_update_with_dsacert() { + let certificate = "-----BEGIN CERTIFICATE----- +MIIEkzCCBEGgAwIBAgIUHw8WUb9beKVreRngMZyZUrP8UqgwCwYJYIZIAWUDBAMC +MEUxCzAJBgNVBAYTAkNOMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJ +bnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMjUxMTE4MTI1MjUxWhcNMjYxMTE4 +MTI1MjUxWjBFMQswCQYDVQQGEwJDTjETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G +A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIDRDCCAjYGByqGSM44BAEw +ggIpAoIBAQCrEexqKCpPOzXK9ZyZZeSLCEFoheoAOMTxLDXcTw9h88J7GZ70udGZ +KinBbT/L9yZimzH3FSwyWbR8gjJxzL84q2qv8rLTtXXgjL93uF+1lDcfITgswCKm +Uvm4uMwPCpved7U+Ni5E5E7FJHEY8MVQQdBnQsKhLpIWyi4xTEK1vHVwS3gyL7D9 +HvxaZInZ5x7tOeqKeNsXOS9zXMdkvhyvfDoMtCrq9CwAd/QNkizQ+jyiQP9/Q8xf +crCp4RMHfMTM+c7KkB34w0FYVWg8GOMpWJw55wQ4VpSCxSrc0JC6159vxtLk/CzW +k9IqfA7f7mr/7sR8StKEhioyQ2Z45Z3nAh0AkhAfQfPdW+1ub60zvSsjfOLso36I +fT7TbWvhAQKCAQEAg7xfk8S1E+4NKgxypwtvvt/FVnfbgbkZ0pkQUrD8bbYZamiN +vdMvDauEj16AEwbvUkLdZa5Ht/EcknYimgGG305Ah/hvqUayVd9C22s/7JxOyg/W +Iy1Y6vScDWNO6Svlu9ZU8RmTEXgL9vIekbo1hAnni3zmC9/cDOuMWVdkL0vWqirw +oY8p2QJ3hJXZTD1pgSQaI9VsnnwU+hmCrhRQQUZf6xcDWe6kpgBnCD3kJu3GDr4I +EsA8Q4rGv+Cr4z29Dl7BiSLnJVSVQ9HOSdqvE73CYHJbeaWk0ASNBkimeqzY+5lP +tqGHoZhDw0V6V9xmE6yA+4RA1lCZSn8aLwoEfQOCAQYAAoIBAQCEWY0OioYoUwTx +qOtrDxUypntePUafTAjp3ZsEy54eLGVBWC4Dd9T78Zn98x/9dt/leH+7f40Dv1tA +d2ok5cjMkeMqUEcq9iEC4SkqZfTg6seaOmMaeOSRSfIw2JR0g8RLUCtEvxyfbBNF +2T6aka4PMqSIx4IPKpRENY2/ICYrFAjTHUFyE7d3ER/zbE+r/J8KpBXi0nDMkYcv +1RkZhkDkxoWnVWO6BPDnbe1yL2linCNUhRtmpYHfe9DO16st7MYBdjLS7YEed9Zv +SoBy1UasYaCEoLROzRuUDWXc+mpY73G6B1cAmZd9OU1d+lQ3K5SdIVSrej5YksTE +5QQsoQlUoyEwHzAdBgNVHQ4EFgQUooMZNGLs7K3I1LZBzsX8gbi3fgEwCwYJYIZI +AWUDBAMCAz8AMDwCHGdHB/xu+SQC92KUgXcW2H67qGTg5jsKYMAJBlcCHEbV7+7K +TdRvdJ2ZKZjSsC39ZPCP7KnaP881vaI= +-----END CERTIFICATE-----"; + let private_key = "-----BEGIN PRIVATE KEY----- +MIICXgIBADCCAjYGByqGSM44BAEwggIpAoIBAQCrEexqKCpPOzXK9ZyZZeSLCEFo +heoAOMTxLDXcTw9h88J7GZ70udGZKinBbT/L9yZimzH3FSwyWbR8gjJxzL84q2qv +8rLTtXXgjL93uF+1lDcfITgswCKmUvm4uMwPCpved7U+Ni5E5E7FJHEY8MVQQdBn +QsKhLpIWyi4xTEK1vHVwS3gyL7D9HvxaZInZ5x7tOeqKeNsXOS9zXMdkvhyvfDoM +tCrq9CwAd/QNkizQ+jyiQP9/Q8xfcrCp4RMHfMTM+c7KkB34w0FYVWg8GOMpWJw5 +5wQ4VpSCxSrc0JC6159vxtLk/CzWk9IqfA7f7mr/7sR8StKEhioyQ2Z45Z3nAh0A +khAfQfPdW+1ub60zvSsjfOLso36IfT7TbWvhAQKCAQEAg7xfk8S1E+4NKgxypwtv +vt/FVnfbgbkZ0pkQUrD8bbYZamiNvdMvDauEj16AEwbvUkLdZa5Ht/EcknYimgGG +305Ah/hvqUayVd9C22s/7JxOyg/WIy1Y6vScDWNO6Svlu9ZU8RmTEXgL9vIekbo1 +hAnni3zmC9/cDOuMWVdkL0vWqirwoY8p2QJ3hJXZTD1pgSQaI9VsnnwU+hmCrhRQ +QUZf6xcDWe6kpgBnCD3kJu3GDr4IEsA8Q4rGv+Cr4z29Dl7BiSLnJVSVQ9HOSdqv +E73CYHJbeaWk0ASNBkimeqzY+5lPtqGHoZhDw0V6V9xmE6yA+4RA1lCZSn8aLwoE +fQQfAh0Ajn5YKCFR13WmbVb52M44GN50U+cJ24RFKPaunA== +-----END PRIVATE KEY-----"; + let mut datakey = get_default_datakey(None, None, None); + datakey.certificate = certificate.as_bytes().to_vec(); + datakey.private_key = private_key.as_bytes().to_vec(); + let _ = X509Plugin::validate_and_update(&mut datakey); + if let Some(value) = datakey.attributes.get("key_length") { + assert_eq!(value, "2048"); + } else { + panic!("Expected key 'key_length' not found in the map."); + } + + if let Some(value) = datakey.attributes.get("key_type") { + assert_eq!(value, "dsa"); + } else { + panic!("Expected key 'key_type' not found in the map."); + } + } + + #[tokio::test] + async fn test_sign_cms_with_timestamp_process_successful() { + let mut parameter = get_default_parameter(); + parameter.insert("sign_type".to_string(), "cms".to_string()); + let content = "hello world".as_bytes(); + let instance = get_default_plugin().await; + let _signature = instance + .sign(content.to_vec(), parameter) + .expect("sign successfully"); + } + #[tokio::test] async fn test_sign_whole_process_successful() { - let parameter = get_default_parameter(); + let mut parameter = get_default_parameter(); + parameter.insert("sign_type".to_string(), "kernel-cms".to_string()); let content = "hello world".as_bytes(); let instance = get_default_plugin().await; let _signature = instance @@ -1210,7 +1476,7 @@ X5BboR/QJakEK+H+EUQAiDs= let sec_datakey = SecDataKey::load(&ca_key, &dummy_engine) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(sec_datakey, None).expect("create plugin successfully"); let ca_content = plugin .generate_keys(&KeyType::X509CA, &infra_config) .expect(format!("generate ca key with no passphrase successfully").as_str()); @@ -1222,7 +1488,7 @@ X5BboR/QJakEK+H+EUQAiDs= let crl_sec_datakey = SecDataKey::load(&ca_key, &dummy_engine) .await .expect("load sec datakey successfully"); - let plugin = X509Plugin::new(crl_sec_datakey).expect("create plugin successfully"); + let plugin = X509Plugin::new(crl_sec_datakey, None).expect("create plugin successfully"); let revoke_time = Utc::now(); let last_update = Utc::now() + Duration::days(1); let next_update = Utc::now() + Duration::days(2); diff --git a/src/presentation/handler/data/sign_handler.rs b/src/presentation/handler/data/sign_handler.rs index b0f144a..dbd0fa9 100644 --- a/src/presentation/handler/data/sign_handler.rs +++ b/src/presentation/handler/data/sign_handler.rs @@ -101,7 +101,7 @@ where let key_id_or_name = request.key_id.to_string(); return match self .key_service - .get_by_type_and_name(request.key_type, request.key_id) + .get_by_type_and_name(Some(request.key_type), request.key_id) .await { Ok(datakey) => { diff --git a/src/util/options.rs b/src/util/options.rs index 399ceeb..8507037 100644 --- a/src/util/options.rs +++ b/src/util/options.rs @@ -19,4 +19,7 @@ pub const KEY_TYPE: &str = "key_type"; pub const SIGN_TYPE: &str = "sign_type"; pub const RPM_V3_SIGNATURE: &str = "rpm_signature_type"; pub const INCLUDE_PARENT_CERT: &str = "include_parent_cert"; +pub const TIMESTAMP_KEY: &str = "timestampe_key"; +pub const TSA_CERT: &str = "tsa_cert"; +pub const TSA_KET: &str = "tsa_key"; pub const CRL: &str = "crl"; diff --git a/src/util/sign.rs b/src/util/sign.rs index 4be005b..38a358e 100644 --- a/src/util/sign.rs +++ b/src/util/sign.rs @@ -5,6 +5,7 @@ use std::str::FromStr; #[derive(clap::ValueEnum, Clone, Debug, PartialEq, Eq, Hash)] pub enum SignType { Cms, // signed method for a CMS signed data + KernelCms, // signed method for a kernel CMS signed data Authenticode, // signed method for signing EFI image using authenticode spec PKCS7, // signed method for a pkcs7 signed data RsaHash, // signed method for a ima eam using rsa hash @@ -14,6 +15,7 @@ impl Display for SignType { fn fmt(&self, f: &mut Formatter) -> fmtResult { match self { SignType::Cms => write!(f, "cms"), + SignType::KernelCms => write!(f, "kernel-cms"), SignType::Authenticode => write!(f, "authenticode"), SignType::PKCS7 => write!(f, "pkcs7"), SignType::RsaHash => write!(f, "rsahash"), @@ -27,6 +29,7 @@ impl FromStr for SignType { fn from_str(s: &str) -> Result { match s { "cms" => Ok(SignType::Cms), + "kernel-cms" => Ok(SignType::KernelCms), "authenticode" => Ok(SignType::Authenticode), "pkcs7" => Ok(SignType::PKCS7), "rsahash" => Ok(SignType::RsaHash), @@ -53,11 +56,7 @@ impl Display for FileType { FileType::KernelModule => write!(f, "ko"), FileType::EfiImage => write!(f, "efi"), FileType::ImaEvm => write!(f, "ima"), -<<<<<<< HEAD - FileType::GenericCms => write!(f, "GenericCms"), -======= FileType::P7s => write!(f, "p7s"), ->>>>>>> d27cb96ece4feb4755163fae3fe7367b451749b8 } } } -- Gitee