From 903ead3f8dfc1dc8ffd864c362d53bddb5b49f58 Mon Sep 17 00:00:00 2001 From: jlcoo Date: Fri, 28 Jun 2024 09:59:12 +0800 Subject: [PATCH 1/3] support ima signature --- Cargo.lock | 1 + Cargo.toml | 1 + src/application/datakey.rs | 31 +++ src/client/cmd/add.rs | 6 +- src/client/file_handler/efi.rs | 16 ++ src/client/file_handler/factory.rs | 2 + src/client/file_handler/ima.rs | 230 ++++++++++++++++++ src/client/file_handler/mod.rs | 1 + src/client/worker/splitter.rs | 4 +- src/infra/sign_plugin/x509.rs | 9 + .../handler/control/datakey_handler.rs | 8 +- src/presentation/handler/data/sign_handler.rs | 38 ++- src/util/attributes.rs | 157 ++++++++++++ src/util/mod.rs | 1 + src/util/sign.rs | 5 + test_assets/private.pem | 28 +++ tools/migrate-pgp-local-keys/import.py | 6 +- 17 files changed, 527 insertions(+), 17 deletions(-) create mode 100644 src/client/file_handler/ima.rs create mode 100644 src/util/attributes.rs create mode 100644 test_assets/private.pem diff --git a/Cargo.lock b/Cargo.lock index a699d64..1e9a28a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4554,6 +4554,7 @@ dependencies = [ "signal-hook", "smallvec", "sqlx", + "tempfile", "testcontainers", "thiserror", "time", diff --git a/Cargo.toml b/Cargo.toml index 2beec63..f69c4a6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,6 +82,7 @@ curve25519-dalek = "~4.0.0" hyper = "~0.14.27" rustls = "~0.21.11" mio = "~0.8.11" +tempfile = "3.3.0" [build-dependencies] diff --git a/src/application/datakey.rs b/src/application/datakey.rs index 3edd2fc..761e523 100644 --- a/src/application/datakey.rs +++ b/src/application/datakey.rs @@ -43,6 +43,7 @@ pub trait KeyService: Send + Sync { async fn get_one(&self, user: Option, id_or_name: String) -> Result; //get keys content async fn export_one(&self, user: Option, id_or_name: String) -> Result; + async fn get_inner_one(&self, id_name: String) -> Result; async fn export_cert_crl( &self, user: Option, @@ -402,6 +403,35 @@ where Ok(key) } + async fn get_inner_one(&self, id_name: String) -> Result { + //NOTE: since the public key or certificate basically will not change at all, we will cache the key here. + if let Some(datakey) = self.container.get_read_datakey(&id_name).await { + return Ok(datakey); + } + let id_or_name = id_name.parse::(); + let mut key: DataKey = match id_or_name { + Ok(id_or_name) => { + self.repository + .get_by_id_or_name(Some(id_or_name), None, false) + .await? + } + Err(_) => { + self.repository + .get_by_id_or_name(None, Some(id_name.clone()), false) + .await? + } + }; + self.sign_service + .read() + .await + .decode_public_keys(&mut key) + .await?; + self.container + .update_read_datakey(&id_name, key.clone()) + .await?; + Ok(key) + } + async fn export_cert_crl( &self, user: Option, @@ -529,6 +559,7 @@ where .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 e495d84..9a8a5f8 100644 --- a/src/client/cmd/add.rs +++ b/src/client/cmd/add.rs @@ -49,6 +49,8 @@ lazy_static! { (FileType::KernelModule, vec!["ko"]), //efi file could be a file without extension (FileType::EfiImage, vec![".*"]), + // ima can be used for any file + (FileType::ImaEvm, vec![".*"]), ]); } @@ -75,7 +77,9 @@ pub struct CommandAdd { #[arg(long)] #[arg(value_enum, default_value_t=SignType::Cms)] #[arg( - help = "specify the signature type, meaningful when key type is x509, EFI file supports `authenticode` only and KO file supports `cms` and `pkcs7`" + help = r#"specify the signature type, meaningful when key type is x509, + EFI file supports `authenticode` only and KO file supports `cms` and `pkcs7` + ima evm file supports `rsa-hash` only"# )] sign_type: SignType, #[arg(long)] diff --git a/src/client/file_handler/efi.rs b/src/client/file_handler/efi.rs index 51c0bcc..ab95b21 100644 --- a/src/client/file_handler/efi.rs +++ b/src/client/file_handler/efi.rs @@ -1,3 +1,19 @@ +/* + * + * * // Copyright (c) 2023 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; diff --git a/src/client/file_handler/factory.rs b/src/client/file_handler/factory.rs index 05e21b7..58e5908 100644 --- a/src/client/file_handler/factory.rs +++ b/src/client/file_handler/factory.rs @@ -17,6 +17,7 @@ use super::efi::EfiFileHandler; use super::generic::GenericFileHandler; use super::kernel_module::KernelModuleFileHandler; +use super::ima::ImaFileHandler; use super::rpm::RpmFileHandler; use super::traits::FileHandler; use crate::util::sign::FileType; @@ -30,6 +31,7 @@ impl FileHandlerFactory { FileType::Generic => Box::new(GenericFileHandler::new()), FileType::KernelModule => Box::new(KernelModuleFileHandler::new()), FileType::EfiImage => Box::new(EfiFileHandler::new()), + FileType::ImaEvm => Box::new(ImaFileHandler::new()), } } } diff --git a/src/client/file_handler/ima.rs b/src/client/file_handler/ima.rs new file mode 100644 index 0000000..e07a6ee --- /dev/null +++ b/src/client/file_handler/ima.rs @@ -0,0 +1,230 @@ +/* + * + * * // Copyright (c) 2023 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::Result; +use crate::util::sign::{KeyType, SignType}; +use async_trait::async_trait; +use std::path::PathBuf; +use tokio::fs; +use std::io::Write; +use uuid::Uuid; + +use crate::util::error::Error; +use crate::util::options; +use crate::util::attributes::PkeyHashAlgo; +use std::collections::HashMap; +use openssl::hash::hash; + +const FILE_EXTENSION: &str = "sig"; +const SUBJECT_KEY_ID: &str = "subject_key"; +const KEY_ID_LEN: &usize = &4; + +#[derive(Clone)] +pub struct ImaFileHandler {} + +impl ImaFileHandler { + pub fn new() -> Self { + Self {} + } +} + +#[derive(Debug)] +struct ImaV2Hdr { + magic: u8, // magic number is 3 + version: u8, // version number is 2 + hash_algo: u8, // hash algorithm sha256 is 4 + keyid: u32, // Subject Key Identifier(SKID) + sig_size: u16, + sig: Vec, +} + +impl ImaV2Hdr { + fn new(algo: u8, keyid: u32, sig: &Vec) -> Self { + ImaV2Hdr { + magic: 3, + version: 2, + hash_algo: algo, + keyid, + sig_size: sig.len() as u16, + sig: sig.clone(), // Clone the vector inside the struct + } + } + + fn serialize(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.push(self.magic); + bytes.push(self.version); + bytes.push(self.hash_algo); + bytes.extend_from_slice(&self.keyid.to_le_bytes()); + bytes.extend_from_slice(&self.sig_size.to_be_bytes()); // sig_size is big-endian + bytes.extend_from_slice(&self.sig); + bytes + } +} + +#[async_trait] +impl FileHandler for ImaFileHandler { + 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( + "ima signer only support detached signature, you may need remove the --detach argument".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( + "ima signer only support x509 key type".to_string(), + )); + } + } + if let Some(sign_type) = sign_options.get(options::SIGN_TYPE) { + if sign_type != SignType::RsaHash.to_string().as_str() { + return Err(Error::InvalidArgumentError( + "ima evm file only support rsahash sign type".to_string(), + )); + } + } + + Ok(()) + } + + async fn split_data( + &self, + path: &PathBuf, + _sign_options: &mut HashMap, + _key_attributes: &HashMap, + ) -> Result>> { + let content = fs::read(path).await?; + // info!("_sign_options: {:?}", _sign_options); + info!("key_attributes: {:?}", _key_attributes); + let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(_key_attributes); + let digest = hash(digest_algo, &content)?; // 完成哈希计算并获取结果 + info!("digest: {:?} hex::encode: {:?}", digest, hex::encode(digest)); + + Ok(vec![digest.to_vec()]) + } + + 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()); + info!("data: {:?}, len {}", data, data[0].len()); + + let skid = _key_attributes.get(SUBJECT_KEY_ID).expect("get skid failed"); + let key_id = match hex::decode(skid) { + Ok(subject_id) => subject_id[subject_id.len() - KEY_ID_LEN..].to_vec(), + Err(e) => return Err(Error::ConvertError(format!("{:?}", e))), + }; + info!("skid: {:?}, key_id {:x?}", skid.clone(), key_id); + let hash_algo = PkeyHashAlgo::get_hash_algo_from_attributes(&_key_attributes); + let hdr = ImaV2Hdr::new(hash_algo.to_u8(), u32::from_le_bytes( + key_id.try_into().unwrap_or_else(|_| panic!("Expected a vector of length 4"))), &data[0]); + + //convert bytes into string + let mut signed = std::fs::File::create(&temp_file)?; + + signed.write_all(&hdr.serialize())?; + Ok(( + temp_file.as_path().display().to_string(), + format!("{}.{}", path.as_path().display(), FILE_EXTENSION), + )) + } +} + +#[cfg(test)] +mod tests { + use crate::{domain::datakey::plugins::x509::X509DigestAlgorithm, util::attributes}; + + use super::*; + use std::collections::HashMap; + use tempfile::TempDir; + use std::panic; + + #[tokio::test] + async fn test_ima_validate_options() { + let handler: ImaFileHandler = ImaFileHandler::new(); + let mut options = HashMap::new(); + options.insert(String::from(options::DETACHED), String::from("true")); + options.insert(String::from(options::KEY_TYPE), KeyType::X509EE.to_string()); + options.insert(String::from(options::SIGN_TYPE), SignType::RsaHash.to_string()); + + assert!(handler.validate_options(&mut options).is_ok()); + + options.insert(String::from(options::DETACHED), String::from("false")); + assert!(handler.validate_options(&mut options).is_err()); + } + + #[tokio::test] + async fn test_ima_split_data() { + let handler: ImaFileHandler = ImaFileHandler::new(); + let temp_file = TempDir::new().unwrap(); + let path = temp_file.path().join("test_file"); + fs::write(&path, b"test data").await.unwrap(); + + + let result = panic::catch_unwind(|| async { + let inner_result = handler.split_data(&path, &mut HashMap::new(), &HashMap::new()).await; + print!("attribute_: {:?}\n", inner_result); + }); + + assert!(result.is_ok()); + + let mut attribute_ = HashMap::new(); + attribute_.insert(String::from(attributes::DIGEST_ALGO), X509DigestAlgorithm::SHA2_256.to_string()); + print!("attribute_: {:?}\n", attribute_); + + let result = handler.split_data(&path, &mut HashMap::new(), &attribute_).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_ima_assemble_data() { + let handler: ImaFileHandler = ImaFileHandler::new(); + let temp_dir = TempDir::new().unwrap(); + let path = PathBuf::from("/tmp/test"); + let data = vec![vec![1, 2, 3, 4, 5]]; + let result = panic::catch_unwind(|| async { + let inner_result = handler.assemble_data(&path, data, &temp_dir.path().to_path_buf(), &HashMap::new(), &HashMap::new()).await; + print!("attribute_: {:?}\n", inner_result); + }); + assert!(result.is_ok()); + + let data = vec![vec![1, 2, 3, 4, 5]]; + let mut attribute_ = HashMap::new(); + attribute_.insert(String::from(SUBJECT_KEY_ID), "0982347ddcf4323d".to_string()); + attribute_.insert(String::from(attributes::DIGEST_ALGO), X509DigestAlgorithm::SHA2_256.to_string()); + let result = handler.assemble_data(&path, data, &temp_dir.path().to_path_buf(), &HashMap::new(), &attribute_).await; + print!("result: {:?}\n", result); + assert!(result.is_ok()); + + let data = vec![vec![1, 2, 3, 4, 5]]; + let mut attribute_ = HashMap::new(); + // error subject key id data + attribute_.insert(String::from(SUBJECT_KEY_ID), "0982347ddcf4323dnn".to_string()); + attribute_.insert(String::from(attributes::DIGEST_ALGO), X509DigestAlgorithm::SHA2_256.to_string()); + let result = handler.assemble_data(&path, data, &temp_dir.path().to_path_buf(), &HashMap::new(), &attribute_).await; + print!("result: {:?}\n", result); + assert!(result.is_err()); + } +} diff --git a/src/client/file_handler/mod.rs b/src/client/file_handler/mod.rs index 0ce9995..0e1c768 100644 --- a/src/client/file_handler/mod.rs +++ b/src/client/file_handler/mod.rs @@ -2,5 +2,6 @@ pub mod efi; pub mod factory; pub mod generic; pub mod kernel_module; +pub mod ima; pub mod rpm; pub mod traits; diff --git a/src/client/worker/splitter.rs b/src/client/worker/splitter.rs index 190a014..ffc9321 100644 --- a/src/client/worker/splitter.rs +++ b/src/client/worker/splitter.rs @@ -44,8 +44,8 @@ impl SignHandler for Splitter { *item.raw_content.borrow_mut() = content; *item.sign_options.borrow_mut() = sign_options; debug!( - "successfully split file {}", - item.file_path.as_path().display() + "successfully split file {} {:?}", + item.file_path.as_path().display(), item.raw_content.as_ref() ); } Err(err) => { diff --git a/src/infra/sign_plugin/x509.rs b/src/infra/sign_plugin/x509.rs index 68032ca..a263c0a 100644 --- a/src/infra/sign_plugin/x509.rs +++ b/src/infra/sign_plugin/x509.rs @@ -53,6 +53,7 @@ use crate::domain::sign_plugin::SignPlugins; use crate::util::error::{Error, Result}; use crate::util::key::{decode_hex_string_to_u8, encode_u8_to_hex_string}; use crate::util::options; +use crate::util::attributes; use crate::util::sign::SignType; #[allow(unused_imports)] use enum_iterator::all; @@ -572,6 +573,7 @@ impl SignPlugins for X509Plugin { self.parent_key.clone().unwrap().certificate.unsecure(), )?)?; } + info!("options: {:?} options::SIGN_TYPE: {:?} ", options, options.get(options::SIGN_TYPE)); match SignType::from_str( options .get(options::SIGN_TYPE) @@ -635,6 +637,13 @@ impl SignPlugins for X509Plugin { )?; Ok(cms_signature.to_der()?) } + SignType::RsaHash => { + // https://github.com/sfackler/rust-openssl/blob/1b4c9b0e47aaefb8fb512d97d68a091b4f624812/openssl/src/pkey_ctx.rs#L863 + let mut signature = vec![]; + attributes::do_sign_rsahash(self.private_key.unsecure(), &content, + &self.attributes, &mut signature).unwrap(); + Ok(signature) + } } } diff --git a/src/presentation/handler/control/datakey_handler.rs b/src/presentation/handler/control/datakey_handler.rs index 64f2949..51e13a5 100644 --- a/src/presentation/handler/control/datakey_handler.rs +++ b/src/presentation/handler/control/datakey_handler.rs @@ -395,9 +395,9 @@ async fn export_public_key( let data_key = key_service .export_one(user, id_or_name.into_inner()) .await?; - if data_key.key_type != KeyType::OpenPGP { - return Ok(HttpResponse::Forbidden().finish()); - } + // if data_key.key_type != KeyType::OpenPGP { + // return Ok(HttpResponse::Forbidden().finish()); + // } Ok(HttpResponse::Ok() .content_type("text/plain") .body(PublicKeyContent::try_from(data_key)?.content)) @@ -426,6 +426,7 @@ async fn export_public_key( (status = 500, description = "Server internal error", body = ErrorMessage) ) )] + async fn export_certificate( user: Option, key_service: web::Data, @@ -437,6 +438,7 @@ async fn export_certificate( if data_key.key_type == KeyType::OpenPGP { return Ok(HttpResponse::Forbidden().finish()); } + info!("certificate: {}", String::from_utf8_lossy(&data_key.certificate)); Ok(HttpResponse::Ok() .content_type("text/plain") .body(CertificateContent::try_from(data_key)?.content)) diff --git a/src/presentation/handler/data/sign_handler.rs b/src/presentation/handler/data/sign_handler.rs index 7d2295c..d13bb91 100644 --- a/src/presentation/handler/data/sign_handler.rs +++ b/src/presentation/handler/data/sign_handler.rs @@ -14,8 +14,6 @@ * */ -use std::collections::HashMap; - pub mod signatrust { tonic::include_proto!("signatrust"); } @@ -30,6 +28,11 @@ use signatrust::{ GetKeyInfoResponse, SignStreamRequest, SignStreamResponse, }; use tonic::{Request, Response, Status, Streaming}; +use openssl::x509::X509; +use crate::domain::datakey::entity::KeyType::X509EE; +use std::collections::HashMap; + +const SUBJECT_KEY_ID: &str = "subject_key"; pub struct SignHandler where @@ -95,21 +98,40 @@ where error: err.to_string(), })); } + 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) .await { - Ok(datakey) => Ok(Response::new(GetKeyInfoResponse { - attributes: datakey.attributes, - error: "".to_string(), - })), + Ok(datakey) => { + let mut new_info = datakey.attributes.clone(); + if datakey.key_type == X509EE { + // need get decode datakey + let public_datakey = match self.key_service.get_inner_one(key_id_or_name).await { + Ok(public) => public, + Err(err) => return Ok(Response::new(GetKeyInfoResponse { + attributes: HashMap::new(), + error: err.to_string(), + })), + }; + let x509 = X509::from_pem(&public_datakey.certificate).expect("can not get certificate from PEM"); + let skid_pem = x509.subject_key_id().expect("get subject key id failed"); + let skid_vec = skid_pem.as_slice(); + new_info.insert(SUBJECT_KEY_ID.to_string(), hex::encode(skid_vec)); + debug!("SKID (hex): {}", hex::encode(skid_vec)); + } + Ok(Response::new(GetKeyInfoResponse { + attributes: new_info, + error: "".to_string(),})) + }, Err(err) => Ok(Response::new(GetKeyInfoResponse { attributes: HashMap::new(), error: err.to_string(), })), }; } + async fn sign_stream( &self, request: Request>, @@ -136,8 +158,8 @@ where })); } debug!( - "begin to sign key_type :{} key_name: {}", - key_type, key_name + "begin to sign key_type :{} key_name: {} data hex: {}", + key_type, key_name, hex::encode(&data) ); match self .key_service diff --git a/src/util/attributes.rs b/src/util/attributes.rs new file mode 100644 index 0000000..912d1d8 --- /dev/null +++ b/src/util/attributes.rs @@ -0,0 +1,157 @@ +/* + * + * * // Copyright (c) 2023 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 std::collections::HashMap; + +use openssl::pkey::PKey; +use openssl::rsa::Rsa; +use openssl::md::Md; +use openssl::pkey_ctx::PkeyCtx; +use openssl::hash::MessageDigest; + +pub const DIGEST_ALGO: &str = "digest_algorithm"; + +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum PkeyHashAlgo { + Md4 = 0, + Md5 = 1, + Sha1 = 2, + RipeMd160 = 3, + Sha256 = 4, + Sha384 = 5, + Sha512 = 6, + Sha224 = 7, + RipeMd128 = 8, + RipeMd256 = 9, + RipeMd320 = 10, + Wp256 = 11, + Wp384 = 12, + Wp512 = 13, + Tgr128 = 14, + Tgr160 = 15, + Tgr192 = 16, + Sm3256 = 17, + Streebog256 = 18, + Streebog512 = 19, +} + +impl PkeyHashAlgo { + pub fn get_hash_algo_from_attributes(attributes: &HashMap) -> PkeyHashAlgo { + let hash_algo = match attributes.get(DIGEST_ALGO).expect("get algo failed").as_str() { + "md5" => PkeyHashAlgo::Md5, + "sha1" => PkeyHashAlgo::Sha1, + "sha2_224" => PkeyHashAlgo::Sha224, + "sha2_256" => PkeyHashAlgo::Sha256, + "sha2_384"=> PkeyHashAlgo::Sha384, + "sha2_512" => PkeyHashAlgo::Sha512, + _ => PkeyHashAlgo::Sha256, + }; + hash_algo + } + + pub fn get_digest_algo_from_attributes(attributes: &HashMap) -> MessageDigest { + let digest_algo = match attributes.get(DIGEST_ALGO).expect("get algo failed").as_str() { + "md5" => MessageDigest::md5(), + "sha1" => MessageDigest::sha1(), + "sha2_224" => MessageDigest::sha224(), + "sha2_256" => MessageDigest::sha256(), + "sha2_384"=> MessageDigest::sha384(), + "sha2_512" => MessageDigest::sha512(), + _ => MessageDigest::sha256(), + }; + digest_algo + } + + pub fn to_u8(&self) -> u8 { + *self as u8 + } +} + +pub fn do_sign_rsahash(pkey_input: &[u8], data: &[u8], attributes: &HashMap, signature: &mut Vec) -> Result<(), Box> { + let digest_algo = match attributes.get(DIGEST_ALGO).expect("get algo failed").as_str() { + "md5" => Md::md5(), + "sha1" => Md::sha1(), + "sha2_224" => Md::sha224(), + "sha2_256" => Md::sha256(), + "sha2_384"=> Md::sha384(), + "sha2_512" => Md::sha512(), + _ => Md::sha256(), + }; + let rsa = Rsa::private_key_from_pem(pkey_input).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + + let mut ctx = PkeyCtx::new(&pkey).unwrap(); + ctx.sign_init().unwrap(); + ctx.set_signature_md(digest_algo).unwrap(); + + ctx.sign_to_vec(&data, signature).unwrap(); + debug!("Signature: {:?} hex::encode(): {:?}", signature, hex::encode(&signature)); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashMap; + use std::env; + use std::fs::read; + use std::panic; + + // 测试数据 + const TEST_DATA: &[u8] = b"Hello, world!"; + + #[tokio::test] + async fn test_attributes_do_sign_rsahash() { + let attributes = HashMap::from([ + (DIGEST_ALGO.to_string(), "sha2_256".to_string()), + ]); + + let result = panic::catch_unwind(|| async { + let current_dir = env::current_dir().expect("get current dir failed"); + let signature_buf = read(current_dir.join("test_assets").join("private.pem")).unwrap(); + let mut signature = Vec::new(); + // 使用私钥进行签名 + let inner_result = do_sign_rsahash(&signature_buf, TEST_DATA, &attributes, &mut signature); + print!("attribute_: {:?}\n", inner_result); + }); + assert!(result.is_ok()); + + // 验证签名的正确性(这部分需要额外的代码来验证签名,这里仅示例) + // 假设有一个验证签名的函数 verify_signature + // assert!(verify_signature(PRIVATE_KEY_PEM, TEST_DATA, &signature)); + } + + #[test] + fn test_attributes_pkey_hash_algo_methods() { + let attributes = HashMap::from([ + (DIGEST_ALGO.to_string(), "sha1".to_string()), + ]); + + // 测试 get_hash_algo_from_attributes + let hash_algo = PkeyHashAlgo::get_hash_algo_from_attributes(&attributes); + assert_eq!(hash_algo, PkeyHashAlgo::Sha1); + + // 测试 get_digest_algo_from_attributes + let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(&attributes); + assert_eq!(digest_algo.block_size(), 64); + + // 测试 to_u8 + let u8_value = hash_algo.to_u8(); + assert_eq!(u8_value, 2); // Sha1 对应的值是 2 + } +} \ No newline at end of file diff --git a/src/util/mod.rs b/src/util/mod.rs index 100185a..85f64c8 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -1,3 +1,4 @@ +pub mod attributes; pub mod cache; pub mod config; pub mod error; diff --git a/src/util/sign.rs b/src/util/sign.rs index 56b6fa4..7076736 100644 --- a/src/util/sign.rs +++ b/src/util/sign.rs @@ -7,6 +7,7 @@ pub enum SignType { Cms, // signed method for a 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 } impl Display for SignType { @@ -15,6 +16,7 @@ impl Display for SignType { SignType::Cms => write!(f, "cms"), SignType::Authenticode => write!(f, "authenticode"), SignType::PKCS7 => write!(f, "pkcs7"), + SignType::RsaHash => write!(f, "rsahash"), } } } @@ -27,6 +29,7 @@ impl FromStr for SignType { "cms" => Ok(SignType::Cms), "authenticode" => Ok(SignType::Authenticode), "pkcs7" => Ok(SignType::PKCS7), + "rsahash" => Ok(SignType::RsaHash), _ => Err(Error::ParameterError("Invalid sign_type param".to_string())), } } @@ -38,6 +41,7 @@ pub enum FileType { Generic, KernelModule, EfiImage, + ImaEvm, } impl Display for FileType { @@ -47,6 +51,7 @@ impl Display for FileType { FileType::Generic => write!(f, "generic"), FileType::KernelModule => write!(f, "ko"), FileType::EfiImage => write!(f, "efi"), + FileType::ImaEvm => write!(f, "ima"), } } } diff --git a/test_assets/private.pem b/test_assets/private.pem new file mode 100644 index 0000000..a29cd9e --- /dev/null +++ b/test_assets/private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4vdyRoi6qthmV +NlAPLVzNzu81tr7hOl4wLniKdVSiZU3fGLQJZ6b4XEes7CRTO+mxDwTP7noR7DKv +L0/T/w23fM77uXcbUHJgd8f/m2W+3We8nGr0N9Husk4mv0JLKt3DNDCvTvw3zJvS +idJHXylQ6rNE52mGsB4GQ+0z0nI86QexfjDfmYbnRn8VkEg+TLWLFvdY0YK3M1el +uUQdRi1B4Lk0U9VcF1GDErYrDhzCJgwtffbfffZ6x1cmFM5qUxmoj8JFAMSFkGH9 +TwCqnc4b7UAFHIMNIwwk+OULa+yEvPkhO4KeZVdYAuvQhrDG+yBl0Di3hzjtnmsV +CAmC40ABAgMBAAECggEAGtX5pG4auVGahcJN/uZoBL16oiLZt33zEKyvde11TeHJ +Xg8ImBUmfA1rnALxg+XAHKhB2rKZqODWu/vzbKgwJedLaeWXF7Cn7Lt5offA3+jp +mMxf/Pne1P2xvI8r4h9n92Mtb0nZ+YEPYZYDpbp9FfQB9pBnE9mhgud7HXwEfDRd +wirya7VvzgfgR80JIqvb4OjkDmfo+GIJrTCXIYrOKaKn6KDqYT6LlDrOESYAYmUY +5o6ArTTgHS9FFJrDkrxKn0cj4Oj6WpchAx5Rzbgnsc+QE/H1tFoV3XwB90JaF7cU +r7GdMdR5z6LRPmfVjQ386IvZpNS5++Ct0fUem6prwQKBgQD7RpfnLdGvmLzYiKkj +G4zM0r5CrM92Zu809SrSzfGr1FeQwSCeTV72Wt9S4DjEtUR54MFodqR+hRnGBoEz +ahikiq2DXLdyzmwxTouidkDaN+p3rNJVDZWhPC1BPYNxJlC6s+oAkvFrhYyL98yc +2W8X8faUflb7jhSJ1lDhSx5ROwKBgQC8NwkD0IwoGdckMVTVjAbwjTFrVTSel2RZ +WjrbhlmtMbVutTi6Oe+Dhq89G9OU65q01jfYGRqCOvcUPv06QuvK2yW13ufHgi40 +R4VHgfiO8ZVyoWC5Ou7PYoGmum5BA5urGT/ziebqa7kKMYuBFJrS7PIbYolVc3lE +/hk9R3If8wKBgFI1/IkUgqBS9qZZwAzOfLie0sT5yT7R2r2W4RXrb4BqjuO7bDKe +2cz8WkjBN5GURP0jPeQxvQvv/Ei/5BGwSrO6854g/ZxAUUGokyLqUbmTxdC16HMP +aL9B8wR/Xbvtb0hGiO77JKWrIhrBTl6a/S/2aFiZJVhUWf/m8LtlWlv1AoGBAJQH +VPfDFfiUGjlE80vhDbQz2CJqqS/yMeJ0rMXrLKyi4+dmcG9HEbGIRFrzf70ocEy6 +DsHPPKIgbiNmRxODfp/VLBuWbQTkxiIn/hL3jd4e80Ns2P3V8+z3tLDE/+HyTISN +H1cTpqHIh4gU33tuETbDVMdlAE5Mu5wfnQotHv0xAoGAYWT41wsBzUQrIh9LS5/M +P5lsCIHdwKf6SD02htP0Y6JnUFmfIGu88MDOaDMwmk7ErILE39Idi/9uUPapfn6M +pmh5efaaojA4LWJw/J7r+BDTT7gjS8B+AVrSgBtW6UObBJqctmsagGDIwj0wY23j +JIcxGkQehuktScMEDbLcG5o= +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/tools/migrate-pgp-local-keys/import.py b/tools/migrate-pgp-local-keys/import.py index 8e819e6..2582ea1 100644 --- a/tools/migrate-pgp-local-keys/import.py +++ b/tools/migrate-pgp-local-keys/import.py @@ -62,11 +62,11 @@ class KeyImport: "digest_algorithm": "sha2_256", "key_type": "rsa", "passphrase": self.passphrase, - "key_length": "3072", + "key_length": "2048", "expire_at": "2050-12-30 00:00:57+08:00", }, 'name': self.name, - 'key_type': 'pgp', + 'key_type': 'x509ee', 'visibility': 'public', 'email': self.email, "description": "imported from EUR server", @@ -91,7 +91,7 @@ class KeyImport: if __name__ == "__main__": if len(sys.argv) != 7: - print("please use file as following: python migrate.py ") + print("please use file as following: python import.py ") else: worker = KeyImport(sys.argv[1], sys.argv[2], sys.argv[3], sys.argv[4], sys.argv[5], sys.argv[6], sys.argv[7]) worker.import_key() -- Gitee From 0e230feca07414ef221f3cb7e2a20eafceb7826e Mon Sep 17 00:00:00 2001 From: jlcoo Date: Fri, 28 Jun 2024 16:44:52 +0800 Subject: [PATCH 2/3] fix fmt --- src/client/file_handler/factory.rs | 2 +- src/client/file_handler/ima.rs | 507 ++++++++++-------- src/client/file_handler/kernel_module.rs | 14 +- src/client/file_handler/mod.rs | 2 +- src/client/file_handler/rpm.rs | 2 +- src/client/worker/splitter.rs | 3 +- src/domain/datakey/plugins/x509.rs | 8 +- .../database/model/datakey/repository.rs | 8 +- src/infra/sign_plugin/x509.rs | 13 +- .../handler/control/datakey_handler.rs | 7 +- src/presentation/handler/data/sign_handler.rs | 33 +- src/util/attributes.rs | 332 ++++++------ src/util/key.rs | 2 +- 13 files changed, 502 insertions(+), 431 deletions(-) diff --git a/src/client/file_handler/factory.rs b/src/client/file_handler/factory.rs index 58e5908..537fa9b 100644 --- a/src/client/file_handler/factory.rs +++ b/src/client/file_handler/factory.rs @@ -16,8 +16,8 @@ use super::efi::EfiFileHandler; use super::generic::GenericFileHandler; -use super::kernel_module::KernelModuleFileHandler; use super::ima::ImaFileHandler; +use super::kernel_module::KernelModuleFileHandler; use super::rpm::RpmFileHandler; use super::traits::FileHandler; use crate::util::sign::FileType; diff --git a/src/client/file_handler/ima.rs b/src/client/file_handler/ima.rs index e07a6ee..cc24821 100644 --- a/src/client/file_handler/ima.rs +++ b/src/client/file_handler/ima.rs @@ -1,230 +1,277 @@ -/* - * - * * // Copyright (c) 2023 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::Result; -use crate::util::sign::{KeyType, SignType}; -use async_trait::async_trait; -use std::path::PathBuf; -use tokio::fs; -use std::io::Write; -use uuid::Uuid; - -use crate::util::error::Error; -use crate::util::options; -use crate::util::attributes::PkeyHashAlgo; -use std::collections::HashMap; -use openssl::hash::hash; - -const FILE_EXTENSION: &str = "sig"; -const SUBJECT_KEY_ID: &str = "subject_key"; -const KEY_ID_LEN: &usize = &4; - -#[derive(Clone)] -pub struct ImaFileHandler {} - -impl ImaFileHandler { - pub fn new() -> Self { - Self {} - } -} - -#[derive(Debug)] -struct ImaV2Hdr { - magic: u8, // magic number is 3 - version: u8, // version number is 2 - hash_algo: u8, // hash algorithm sha256 is 4 - keyid: u32, // Subject Key Identifier(SKID) - sig_size: u16, - sig: Vec, -} - -impl ImaV2Hdr { - fn new(algo: u8, keyid: u32, sig: &Vec) -> Self { - ImaV2Hdr { - magic: 3, - version: 2, - hash_algo: algo, - keyid, - sig_size: sig.len() as u16, - sig: sig.clone(), // Clone the vector inside the struct - } - } - - fn serialize(&self) -> Vec { - let mut bytes = Vec::new(); - bytes.push(self.magic); - bytes.push(self.version); - bytes.push(self.hash_algo); - bytes.extend_from_slice(&self.keyid.to_le_bytes()); - bytes.extend_from_slice(&self.sig_size.to_be_bytes()); // sig_size is big-endian - bytes.extend_from_slice(&self.sig); - bytes - } -} - -#[async_trait] -impl FileHandler for ImaFileHandler { - 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( - "ima signer only support detached signature, you may need remove the --detach argument".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( - "ima signer only support x509 key type".to_string(), - )); - } - } - if let Some(sign_type) = sign_options.get(options::SIGN_TYPE) { - if sign_type != SignType::RsaHash.to_string().as_str() { - return Err(Error::InvalidArgumentError( - "ima evm file only support rsahash sign type".to_string(), - )); - } - } - - Ok(()) - } - - async fn split_data( - &self, - path: &PathBuf, - _sign_options: &mut HashMap, - _key_attributes: &HashMap, - ) -> Result>> { - let content = fs::read(path).await?; - // info!("_sign_options: {:?}", _sign_options); - info!("key_attributes: {:?}", _key_attributes); - let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(_key_attributes); - let digest = hash(digest_algo, &content)?; // 完成哈希计算并获取结果 - info!("digest: {:?} hex::encode: {:?}", digest, hex::encode(digest)); - - Ok(vec![digest.to_vec()]) - } - - 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()); - info!("data: {:?}, len {}", data, data[0].len()); - - let skid = _key_attributes.get(SUBJECT_KEY_ID).expect("get skid failed"); - let key_id = match hex::decode(skid) { - Ok(subject_id) => subject_id[subject_id.len() - KEY_ID_LEN..].to_vec(), - Err(e) => return Err(Error::ConvertError(format!("{:?}", e))), - }; - info!("skid: {:?}, key_id {:x?}", skid.clone(), key_id); - let hash_algo = PkeyHashAlgo::get_hash_algo_from_attributes(&_key_attributes); - let hdr = ImaV2Hdr::new(hash_algo.to_u8(), u32::from_le_bytes( - key_id.try_into().unwrap_or_else(|_| panic!("Expected a vector of length 4"))), &data[0]); - - //convert bytes into string - let mut signed = std::fs::File::create(&temp_file)?; - - signed.write_all(&hdr.serialize())?; - Ok(( - temp_file.as_path().display().to_string(), - format!("{}.{}", path.as_path().display(), FILE_EXTENSION), - )) - } -} - -#[cfg(test)] -mod tests { - use crate::{domain::datakey::plugins::x509::X509DigestAlgorithm, util::attributes}; - - use super::*; - use std::collections::HashMap; - use tempfile::TempDir; - use std::panic; - - #[tokio::test] - async fn test_ima_validate_options() { - let handler: ImaFileHandler = ImaFileHandler::new(); - let mut options = HashMap::new(); - options.insert(String::from(options::DETACHED), String::from("true")); - options.insert(String::from(options::KEY_TYPE), KeyType::X509EE.to_string()); - options.insert(String::from(options::SIGN_TYPE), SignType::RsaHash.to_string()); - - assert!(handler.validate_options(&mut options).is_ok()); - - options.insert(String::from(options::DETACHED), String::from("false")); - assert!(handler.validate_options(&mut options).is_err()); - } - - #[tokio::test] - async fn test_ima_split_data() { - let handler: ImaFileHandler = ImaFileHandler::new(); - let temp_file = TempDir::new().unwrap(); - let path = temp_file.path().join("test_file"); - fs::write(&path, b"test data").await.unwrap(); - - - let result = panic::catch_unwind(|| async { - let inner_result = handler.split_data(&path, &mut HashMap::new(), &HashMap::new()).await; - print!("attribute_: {:?}\n", inner_result); - }); - - assert!(result.is_ok()); - - let mut attribute_ = HashMap::new(); - attribute_.insert(String::from(attributes::DIGEST_ALGO), X509DigestAlgorithm::SHA2_256.to_string()); - print!("attribute_: {:?}\n", attribute_); - - let result = handler.split_data(&path, &mut HashMap::new(), &attribute_).await; - assert!(result.is_ok()); - } - - #[tokio::test] - async fn test_ima_assemble_data() { - let handler: ImaFileHandler = ImaFileHandler::new(); - let temp_dir = TempDir::new().unwrap(); - let path = PathBuf::from("/tmp/test"); - let data = vec![vec![1, 2, 3, 4, 5]]; - let result = panic::catch_unwind(|| async { - let inner_result = handler.assemble_data(&path, data, &temp_dir.path().to_path_buf(), &HashMap::new(), &HashMap::new()).await; - print!("attribute_: {:?}\n", inner_result); - }); - assert!(result.is_ok()); - - let data = vec![vec![1, 2, 3, 4, 5]]; - let mut attribute_ = HashMap::new(); - attribute_.insert(String::from(SUBJECT_KEY_ID), "0982347ddcf4323d".to_string()); - attribute_.insert(String::from(attributes::DIGEST_ALGO), X509DigestAlgorithm::SHA2_256.to_string()); - let result = handler.assemble_data(&path, data, &temp_dir.path().to_path_buf(), &HashMap::new(), &attribute_).await; - print!("result: {:?}\n", result); - assert!(result.is_ok()); - - let data = vec![vec![1, 2, 3, 4, 5]]; - let mut attribute_ = HashMap::new(); - // error subject key id data - attribute_.insert(String::from(SUBJECT_KEY_ID), "0982347ddcf4323dnn".to_string()); - attribute_.insert(String::from(attributes::DIGEST_ALGO), X509DigestAlgorithm::SHA2_256.to_string()); - let result = handler.assemble_data(&path, data, &temp_dir.path().to_path_buf(), &HashMap::new(), &attribute_).await; - print!("result: {:?}\n", result); - assert!(result.is_err()); - } -} +/* + * + * * // Copyright (c) 2023 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::Result; +use crate::util::sign::{KeyType, SignType}; +use async_trait::async_trait; +use std::io::Write; +use std::path::PathBuf; +use tokio::fs; +use uuid::Uuid; + +use crate::util::attributes::PkeyHashAlgo; +use crate::util::error::Error; +use crate::util::options; +use openssl::hash::hash; +use std::collections::HashMap; + +const FILE_EXTENSION: &str = "sig"; +const SUBJECT_KEY_ID: &str = "subject_key"; +const KEY_ID_LEN: &usize = &4; + +#[derive(Clone)] +pub struct ImaFileHandler {} + +impl ImaFileHandler { + pub fn new() -> Self { + Self {} + } +} + +#[derive(Debug)] +struct ImaV2Hdr { + magic: u8, // magic number is 3 + version: u8, // version number is 2 + hash_algo: u8, // hash algorithm sha256 is 4 + keyid: u32, // Subject Key Identifier(SKID) + sig_size: u16, + sig: Vec, +} + +impl ImaV2Hdr { + fn new(algo: u8, keyid: u32, sig: &Vec) -> Self { + ImaV2Hdr { + magic: 3, + version: 2, + hash_algo: algo, + keyid, + sig_size: sig.len() as u16, + sig: sig.clone(), // Clone the vector inside the struct + } + } + + fn serialize(&self) -> Vec { + let mut bytes = Vec::new(); + bytes.push(self.magic); + bytes.push(self.version); + bytes.push(self.hash_algo); + bytes.extend_from_slice(&self.keyid.to_le_bytes()); + bytes.extend_from_slice(&self.sig_size.to_be_bytes()); // sig_size is big-endian + bytes.extend_from_slice(&self.sig); + bytes + } +} + +#[async_trait] +impl FileHandler for ImaFileHandler { + 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( + "ima signer only support detached signature, you may need remove the --detach argument".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( + "ima signer only support x509 key type".to_string(), + )); + } + } + if let Some(sign_type) = sign_options.get(options::SIGN_TYPE) { + if sign_type != SignType::RsaHash.to_string().as_str() { + return Err(Error::InvalidArgumentError( + "ima evm file only support rsahash sign type".to_string(), + )); + } + } + + Ok(()) + } + + async fn split_data( + &self, + path: &PathBuf, + _sign_options: &mut HashMap, + _key_attributes: &HashMap, + ) -> Result>> { + let content = fs::read(path).await?; + debug!("key_attributes: {:?}", _key_attributes); + let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(_key_attributes); + let digest = hash(digest_algo, &content)?; // 完成哈希计算并获取结果 + + Ok(vec![digest.to_vec()]) + } + + 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()); + let skid = _key_attributes + .get(SUBJECT_KEY_ID) + .expect("get skid failed"); + let key_id = match hex::decode(skid) { + Ok(subject_id) => subject_id[subject_id.len() - KEY_ID_LEN..].to_vec(), + Err(e) => return Err(Error::ConvertError(format!("{:?}", e))), + }; + debug!("skid: {:?}, key_id {:x?}", skid.clone(), key_id); + let hash_algo = PkeyHashAlgo::get_hash_algo_from_attributes(_key_attributes); + let hdr = ImaV2Hdr::new( + hash_algo.to_u8(), + u32::from_le_bytes( + key_id + .try_into() + .unwrap_or_else(|_| panic!("Expected a vector of length 4")), + ), + &data[0], + ); + + //convert bytes into string + let mut signed = std::fs::File::create(&temp_file)?; + + signed.write_all(&hdr.serialize())?; + Ok(( + temp_file.as_path().display().to_string(), + format!("{}.{}", path.as_path().display(), FILE_EXTENSION), + )) + } +} + +#[cfg(test)] +mod tests { + use crate::{domain::datakey::plugins::x509::X509DigestAlgorithm, util::attributes}; + + use super::*; + use std::collections::HashMap; + use std::panic; + use tempfile::TempDir; + + #[tokio::test] + async fn test_ima_validate_options() { + let handler: ImaFileHandler = ImaFileHandler::new(); + let mut options = HashMap::new(); + options.insert(String::from(options::DETACHED), String::from("true")); + options.insert(String::from(options::KEY_TYPE), KeyType::X509EE.to_string()); + options.insert( + String::from(options::SIGN_TYPE), + SignType::RsaHash.to_string(), + ); + + assert!(handler.validate_options(&mut options).is_ok()); + + options.insert(String::from(options::DETACHED), String::from("false")); + assert!(handler.validate_options(&mut options).is_err()); + } + + #[tokio::test] + async fn test_ima_split_data() { + let handler: ImaFileHandler = ImaFileHandler::new(); + let temp_file = TempDir::new().unwrap(); + let path = temp_file.path().join("test_file"); + fs::write(&path, b"test data").await.unwrap(); + + let result = panic::catch_unwind(|| async { + let inner_result = handler + .split_data(&path, &mut HashMap::new(), &HashMap::new()) + .await; + print!("attribute_: {:?}\n", inner_result); + }); + + assert!(result.is_ok()); + + let mut attribute_ = HashMap::new(); + attribute_.insert( + String::from(attributes::DIGEST_ALGO), + X509DigestAlgorithm::SHA2_256.to_string(), + ); + print!("attribute_: {:?}\n", attribute_); + + let result = handler + .split_data(&path, &mut HashMap::new(), &attribute_) + .await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn test_ima_assemble_data() { + let handler: ImaFileHandler = ImaFileHandler::new(); + let temp_dir = TempDir::new().unwrap(); + let path = PathBuf::from("/tmp/test"); + let data = vec![vec![1, 2, 3, 4, 5]]; + let result = panic::catch_unwind(|| async { + let inner_result = handler + .assemble_data( + &path, + data, + &temp_dir.path().to_path_buf(), + &HashMap::new(), + &HashMap::new(), + ) + .await; + print!("attribute_: {:?}\n", inner_result); + }); + assert!(result.is_ok()); + + let data = vec![vec![1, 2, 3, 4, 5]]; + let mut attribute_ = HashMap::new(); + attribute_.insert(String::from(SUBJECT_KEY_ID), "0982347ddcf4323d".to_string()); + attribute_.insert( + String::from(attributes::DIGEST_ALGO), + X509DigestAlgorithm::SHA2_256.to_string(), + ); + let result = handler + .assemble_data( + &path, + data, + &temp_dir.path().to_path_buf(), + &HashMap::new(), + &attribute_, + ) + .await; + print!("result: {:?}\n", result); + assert!(result.is_ok()); + + let data = vec![vec![1, 2, 3, 4, 5]]; + let mut attribute_ = HashMap::new(); + // error subject key id data + attribute_.insert( + String::from(SUBJECT_KEY_ID), + "0982347ddcf4323dnn".to_string(), + ); + attribute_.insert( + String::from(attributes::DIGEST_ALGO), + X509DigestAlgorithm::SHA2_256.to_string(), + ); + let result = handler + .assemble_data( + &path, + data, + &temp_dir.path().to_path_buf(), + &HashMap::new(), + &attribute_, + ) + .await; + print!("result: {:?}\n", result); + assert!(result.is_err()); + } +} diff --git a/src/client/file_handler/kernel_module.rs b/src/client/file_handler/kernel_module.rs index e11896a..081a155 100644 --- a/src/client/file_handler/kernel_module.rs +++ b/src/client/file_handler/kernel_module.rs @@ -86,7 +86,7 @@ impl KernelModuleFileHandler { signature: &[u8], ) -> Result<()> { let mut signed = fs::File::create(tempfile)?; - signed.write_all(&self.get_raw_content(module, &mut HashMap::new())?)?; + signed.write_all(&self.get_raw_content(module, &HashMap::new())?)?; signed.write_all(signature)?; let sig_struct = ModuleSignature::new(signature.len() as c_uint); signed.write_all(&bincode::encode_to_vec( @@ -102,7 +102,7 @@ impl KernelModuleFileHandler { pub fn get_raw_content( &self, path: &PathBuf, - sign_options: &mut HashMap, + sign_options: &HashMap, ) -> Result> { let raw_content = fs::read(path)?; let mut file = fs::File::open(path)?; @@ -271,7 +271,7 @@ mod test { .expect("generate unsigned kernel module failed"); let path = PathBuf::from(name); let raw_content = file_handler - .get_raw_content(&path, &mut sign_options) + .get_raw_content(&path, &sign_options) .expect("get raw content failed"); assert_eq!(raw_content.len(), SIGNATURE_SIZE - 1); assert_eq!(original_content, raw_content); @@ -285,7 +285,7 @@ mod test { .expect("generate unsigned kernel module failed"); let path = PathBuf::from(name); let raw_content = file_handler - .get_raw_content(&path, &mut sign_options) + .get_raw_content(&path, &sign_options) .expect("get raw content failed"); assert_eq!(raw_content.len(), SIGNATURE_SIZE + 100); assert_eq!(original_content, raw_content); @@ -299,7 +299,7 @@ mod test { .expect("generate signed kernel module failed"); let path = PathBuf::from(name); let raw_content = file_handler - .get_raw_content(&path, &mut sign_options) + .get_raw_content(&path, &sign_options) .expect("get raw content failed"); assert_eq!(raw_content.len(), 100); assert_eq!(original_content, raw_content); @@ -312,7 +312,7 @@ mod test { let (name, _) = generate_signed_kernel_module(100, true).expect("generate signed kernel module failed"); let path = PathBuf::from(name); - let result = file_handler.get_raw_content(&path, &mut sign_options); + let result = file_handler.get_raw_content(&path, &sign_options); assert_eq!( result.unwrap_err().to_string(), "failed to split file: invalid kernel module signature size found" @@ -389,7 +389,7 @@ mod test { assert_eq!(temp_file.starts_with(temp_dir.to_str().unwrap()), true); assert_eq!(file_name, name); let result = handler - .get_raw_content(&PathBuf::from(temp_file), &mut options) + .get_raw_content(&PathBuf::from(temp_file), &options) .expect("get raw content failed"); assert_eq!(result, raw_content); } diff --git a/src/client/file_handler/mod.rs b/src/client/file_handler/mod.rs index 0e1c768..21ccba3 100644 --- a/src/client/file_handler/mod.rs +++ b/src/client/file_handler/mod.rs @@ -1,7 +1,7 @@ pub mod efi; pub mod factory; pub mod generic; -pub mod kernel_module; pub mod ima; +pub mod kernel_module; pub mod rpm; pub mod traits; diff --git a/src/client/file_handler/rpm.rs b/src/client/file_handler/rpm.rs index 950a17c..56d961d 100644 --- a/src/client/file_handler/rpm.rs +++ b/src/client/file_handler/rpm.rs @@ -147,7 +147,7 @@ impl FileHandler for RpmFileHandler { header_and_content_digest, } = Package::create_sig_header_digests( header_bytes.as_slice(), - &package.content.as_slice(), + package.content.as_slice(), )?; let key_type = self.get_key_type(key_attributes); diff --git a/src/client/worker/splitter.rs b/src/client/worker/splitter.rs index ffc9321..f2589b1 100644 --- a/src/client/worker/splitter.rs +++ b/src/client/worker/splitter.rs @@ -45,7 +45,8 @@ impl SignHandler for Splitter { *item.sign_options.borrow_mut() = sign_options; debug!( "successfully split file {} {:?}", - item.file_path.as_path().display(), item.raw_content.as_ref() + item.file_path.as_path().display(), + item.raw_content.as_ref() ); } Err(err) => { diff --git a/src/domain/datakey/plugins/x509.rs b/src/domain/datakey/plugins/x509.rs index 79ad3dd..63dcfbf 100644 --- a/src/domain/datakey/plugins/x509.rs +++ b/src/domain/datakey/plugins/x509.rs @@ -24,19 +24,15 @@ use std::str::FromStr; pub const X509_VALID_KEY_SIZE: [&str; 3] = ["2048", "3072", "4096"]; -#[derive(Debug, Clone, PartialEq, Sequence, Deserialize)] +#[derive(Debug, Clone, PartialEq, Sequence, Deserialize, Default)] pub enum X509EEUsage { #[serde(rename = "efi")] + #[default] Efi, #[serde(rename = "ko")] Ko, } -impl Default for X509EEUsage { - fn default() -> Self { - X509EEUsage::Efi - } -} impl FromStr for X509EEUsage { type Err = Error; fn from_str(s: &str) -> Result { diff --git a/src/infra/database/model/datakey/repository.rs b/src/infra/database/model/datakey/repository.rs index 09f7f3a..bf735c0 100644 --- a/src/infra/database/model/datakey/repository.rs +++ b/src/infra/database/model/datakey/repository.rs @@ -336,10 +336,10 @@ impl<'a> Repository for DataKeyRepository<'a> { async fn get_revoked_serial_number_by_parent_id(&self, id: i32) -> Result> { match revoked_key_dto::Entity::find() .select_only() - .columns(revoked_key_dto::Column::iter().filter(|col| match col { - revoked_key_dto::Column::SerialNumber => false, - _ => true, - })) + .columns( + revoked_key_dto::Column::iter() + .filter(|col| !matches!(col, revoked_key_dto::Column::SerialNumber)), + ) .column_as( Expr::col(( Alias::new("datakey_table"), diff --git a/src/infra/sign_plugin/x509.rs b/src/infra/sign_plugin/x509.rs index a263c0a..5acdc60 100644 --- a/src/infra/sign_plugin/x509.rs +++ b/src/infra/sign_plugin/x509.rs @@ -50,10 +50,10 @@ use crate::domain::datakey::plugins::x509::{ X509DigestAlgorithm, X509EEUsage, X509KeyType, X509_VALID_KEY_SIZE, }; use crate::domain::sign_plugin::SignPlugins; +use crate::util::attributes; use crate::util::error::{Error, Result}; use crate::util::key::{decode_hex_string_to_u8, encode_u8_to_hex_string}; use crate::util::options; -use crate::util::attributes; use crate::util::sign::SignType; #[allow(unused_imports)] use enum_iterator::all; @@ -573,7 +573,7 @@ impl SignPlugins for X509Plugin { self.parent_key.clone().unwrap().certificate.unsecure(), )?)?; } - info!("options: {:?} options::SIGN_TYPE: {:?} ", options, options.get(options::SIGN_TYPE)); + match SignType::from_str( options .get(options::SIGN_TYPE) @@ -640,8 +640,13 @@ impl SignPlugins for X509Plugin { SignType::RsaHash => { // https://github.com/sfackler/rust-openssl/blob/1b4c9b0e47aaefb8fb512d97d68a091b4f624812/openssl/src/pkey_ctx.rs#L863 let mut signature = vec![]; - attributes::do_sign_rsahash(self.private_key.unsecure(), &content, - &self.attributes, &mut signature).unwrap(); + attributes::do_sign_rsahash( + self.private_key.unsecure(), + &content, + &self.attributes, + &mut signature, + ) + .unwrap(); Ok(signature) } } diff --git a/src/presentation/handler/control/datakey_handler.rs b/src/presentation/handler/control/datakey_handler.rs index 51e13a5..a1b1977 100644 --- a/src/presentation/handler/control/datakey_handler.rs +++ b/src/presentation/handler/control/datakey_handler.rs @@ -395,9 +395,9 @@ async fn export_public_key( let data_key = key_service .export_one(user, id_or_name.into_inner()) .await?; - // if data_key.key_type != KeyType::OpenPGP { - // return Ok(HttpResponse::Forbidden().finish()); - // } + if data_key.key_type != KeyType::OpenPGP { + return Ok(HttpResponse::Forbidden().finish()); + } Ok(HttpResponse::Ok() .content_type("text/plain") .body(PublicKeyContent::try_from(data_key)?.content)) @@ -438,7 +438,6 @@ async fn export_certificate( if data_key.key_type == KeyType::OpenPGP { return Ok(HttpResponse::Forbidden().finish()); } - info!("certificate: {}", String::from_utf8_lossy(&data_key.certificate)); Ok(HttpResponse::Ok() .content_type("text/plain") .body(CertificateContent::try_from(data_key)?.content)) diff --git a/src/presentation/handler/data/sign_handler.rs b/src/presentation/handler/data/sign_handler.rs index d13bb91..b0f144a 100644 --- a/src/presentation/handler/data/sign_handler.rs +++ b/src/presentation/handler/data/sign_handler.rs @@ -21,16 +21,16 @@ use tokio_stream::StreamExt; use crate::application::datakey::KeyService; use crate::application::user::UserService; +use crate::domain::datakey::entity::KeyType::X509EE; use crate::util::error::Error; use crate::util::error::Result as SignatrustResult; +use openssl::x509::X509; use signatrust::{ signatrust_server::Signatrust, signatrust_server::SignatrustServer, GetKeyInfoRequest, GetKeyInfoResponse, SignStreamRequest, SignStreamResponse, }; -use tonic::{Request, Response, Status, Streaming}; -use openssl::x509::X509; -use crate::domain::datakey::entity::KeyType::X509EE; use std::collections::HashMap; +use tonic::{Request, Response, Status, Streaming}; const SUBJECT_KEY_ID: &str = "subject_key"; @@ -108,23 +108,28 @@ where let mut new_info = datakey.attributes.clone(); if datakey.key_type == X509EE { // need get decode datakey - let public_datakey = match self.key_service.get_inner_one(key_id_or_name).await { + let public_datakey = match self.key_service.get_inner_one(key_id_or_name).await + { Ok(public) => public, - Err(err) => return Ok(Response::new(GetKeyInfoResponse { - attributes: HashMap::new(), - error: err.to_string(), - })), + Err(err) => { + return Ok(Response::new(GetKeyInfoResponse { + attributes: HashMap::new(), + error: err.to_string(), + })) + } }; - let x509 = X509::from_pem(&public_datakey.certificate).expect("can not get certificate from PEM"); + let x509 = X509::from_pem(&public_datakey.certificate) + .expect("can not get certificate from PEM"); let skid_pem = x509.subject_key_id().expect("get subject key id failed"); let skid_vec = skid_pem.as_slice(); new_info.insert(SUBJECT_KEY_ID.to_string(), hex::encode(skid_vec)); debug!("SKID (hex): {}", hex::encode(skid_vec)); } Ok(Response::new(GetKeyInfoResponse { - attributes: new_info, - error: "".to_string(),})) - }, + attributes: new_info, + error: "".to_string(), + })) + } Err(err) => Ok(Response::new(GetKeyInfoResponse { attributes: HashMap::new(), error: err.to_string(), @@ -158,8 +163,8 @@ where })); } debug!( - "begin to sign key_type :{} key_name: {} data hex: {}", - key_type, key_name, hex::encode(&data) + "begin to sign key_type :{} key_name: {}", + key_type, key_name ); match self .key_service diff --git a/src/util/attributes.rs b/src/util/attributes.rs index 912d1d8..0b1749f 100644 --- a/src/util/attributes.rs +++ b/src/util/attributes.rs @@ -1,157 +1,175 @@ -/* - * - * * // Copyright (c) 2023 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 std::collections::HashMap; - -use openssl::pkey::PKey; -use openssl::rsa::Rsa; -use openssl::md::Md; -use openssl::pkey_ctx::PkeyCtx; -use openssl::hash::MessageDigest; - -pub const DIGEST_ALGO: &str = "digest_algorithm"; - -#[repr(u8)] -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum PkeyHashAlgo { - Md4 = 0, - Md5 = 1, - Sha1 = 2, - RipeMd160 = 3, - Sha256 = 4, - Sha384 = 5, - Sha512 = 6, - Sha224 = 7, - RipeMd128 = 8, - RipeMd256 = 9, - RipeMd320 = 10, - Wp256 = 11, - Wp384 = 12, - Wp512 = 13, - Tgr128 = 14, - Tgr160 = 15, - Tgr192 = 16, - Sm3256 = 17, - Streebog256 = 18, - Streebog512 = 19, -} - -impl PkeyHashAlgo { - pub fn get_hash_algo_from_attributes(attributes: &HashMap) -> PkeyHashAlgo { - let hash_algo = match attributes.get(DIGEST_ALGO).expect("get algo failed").as_str() { - "md5" => PkeyHashAlgo::Md5, - "sha1" => PkeyHashAlgo::Sha1, - "sha2_224" => PkeyHashAlgo::Sha224, - "sha2_256" => PkeyHashAlgo::Sha256, - "sha2_384"=> PkeyHashAlgo::Sha384, - "sha2_512" => PkeyHashAlgo::Sha512, - _ => PkeyHashAlgo::Sha256, - }; - hash_algo - } - - pub fn get_digest_algo_from_attributes(attributes: &HashMap) -> MessageDigest { - let digest_algo = match attributes.get(DIGEST_ALGO).expect("get algo failed").as_str() { - "md5" => MessageDigest::md5(), - "sha1" => MessageDigest::sha1(), - "sha2_224" => MessageDigest::sha224(), - "sha2_256" => MessageDigest::sha256(), - "sha2_384"=> MessageDigest::sha384(), - "sha2_512" => MessageDigest::sha512(), - _ => MessageDigest::sha256(), - }; - digest_algo - } - - pub fn to_u8(&self) -> u8 { - *self as u8 - } -} - -pub fn do_sign_rsahash(pkey_input: &[u8], data: &[u8], attributes: &HashMap, signature: &mut Vec) -> Result<(), Box> { - let digest_algo = match attributes.get(DIGEST_ALGO).expect("get algo failed").as_str() { - "md5" => Md::md5(), - "sha1" => Md::sha1(), - "sha2_224" => Md::sha224(), - "sha2_256" => Md::sha256(), - "sha2_384"=> Md::sha384(), - "sha2_512" => Md::sha512(), - _ => Md::sha256(), - }; - let rsa = Rsa::private_key_from_pem(pkey_input).unwrap(); - let pkey = PKey::from_rsa(rsa).unwrap(); - - let mut ctx = PkeyCtx::new(&pkey).unwrap(); - ctx.sign_init().unwrap(); - ctx.set_signature_md(digest_algo).unwrap(); - - ctx.sign_to_vec(&data, signature).unwrap(); - debug!("Signature: {:?} hex::encode(): {:?}", signature, hex::encode(&signature)); - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - use std::collections::HashMap; - use std::env; - use std::fs::read; - use std::panic; - - // 测试数据 - const TEST_DATA: &[u8] = b"Hello, world!"; - - #[tokio::test] - async fn test_attributes_do_sign_rsahash() { - let attributes = HashMap::from([ - (DIGEST_ALGO.to_string(), "sha2_256".to_string()), - ]); - - let result = panic::catch_unwind(|| async { - let current_dir = env::current_dir().expect("get current dir failed"); - let signature_buf = read(current_dir.join("test_assets").join("private.pem")).unwrap(); - let mut signature = Vec::new(); - // 使用私钥进行签名 - let inner_result = do_sign_rsahash(&signature_buf, TEST_DATA, &attributes, &mut signature); - print!("attribute_: {:?}\n", inner_result); - }); - assert!(result.is_ok()); - - // 验证签名的正确性(这部分需要额外的代码来验证签名,这里仅示例) - // 假设有一个验证签名的函数 verify_signature - // assert!(verify_signature(PRIVATE_KEY_PEM, TEST_DATA, &signature)); - } - - #[test] - fn test_attributes_pkey_hash_algo_methods() { - let attributes = HashMap::from([ - (DIGEST_ALGO.to_string(), "sha1".to_string()), - ]); - - // 测试 get_hash_algo_from_attributes - let hash_algo = PkeyHashAlgo::get_hash_algo_from_attributes(&attributes); - assert_eq!(hash_algo, PkeyHashAlgo::Sha1); - - // 测试 get_digest_algo_from_attributes - let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(&attributes); - assert_eq!(digest_algo.block_size(), 64); - - // 测试 to_u8 - let u8_value = hash_algo.to_u8(); - assert_eq!(u8_value, 2); // Sha1 对应的值是 2 - } -} \ No newline at end of file +/* + * + * * // Copyright (c) 2023 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 std::collections::HashMap; + +use openssl::hash::MessageDigest; +use openssl::md::Md; +use openssl::pkey::PKey; +use openssl::pkey_ctx::PkeyCtx; +use openssl::rsa::Rsa; + +pub const DIGEST_ALGO: &str = "digest_algorithm"; + +#[repr(u8)] +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum PkeyHashAlgo { + Md4 = 0, + Md5 = 1, + Sha1 = 2, + RipeMd160 = 3, + Sha256 = 4, + Sha384 = 5, + Sha512 = 6, + Sha224 = 7, + RipeMd128 = 8, + RipeMd256 = 9, + RipeMd320 = 10, + Wp256 = 11, + Wp384 = 12, + Wp512 = 13, + Tgr128 = 14, + Tgr160 = 15, + Tgr192 = 16, + Sm3256 = 17, + Streebog256 = 18, + Streebog512 = 19, +} + +impl PkeyHashAlgo { + pub fn get_hash_algo_from_attributes(attributes: &HashMap) -> PkeyHashAlgo { + let hash_algo = match attributes + .get(DIGEST_ALGO) + .expect("get algo failed") + .as_str() + { + "md5" => PkeyHashAlgo::Md5, + "sha1" => PkeyHashAlgo::Sha1, + "sha2_224" => PkeyHashAlgo::Sha224, + "sha2_256" => PkeyHashAlgo::Sha256, + "sha2_384" => PkeyHashAlgo::Sha384, + "sha2_512" => PkeyHashAlgo::Sha512, + _ => PkeyHashAlgo::Sha256, + }; + hash_algo + } + + pub fn get_digest_algo_from_attributes(attributes: &HashMap) -> MessageDigest { + let digest_algo = match attributes + .get(DIGEST_ALGO) + .expect("get algo failed") + .as_str() + { + "md5" => MessageDigest::md5(), + "sha1" => MessageDigest::sha1(), + "sha2_224" => MessageDigest::sha224(), + "sha2_256" => MessageDigest::sha256(), + "sha2_384" => MessageDigest::sha384(), + "sha2_512" => MessageDigest::sha512(), + _ => MessageDigest::sha256(), + }; + digest_algo + } + + pub fn to_u8(self) -> u8 { + self as u8 + } +} + +pub fn do_sign_rsahash( + pkey_input: &[u8], + data: &[u8], + attributes: &HashMap, + signature: &mut Vec, +) -> Result<(), Box> { + let digest_algo = match attributes + .get(DIGEST_ALGO) + .expect("get algo failed") + .as_str() + { + "md5" => Md::md5(), + "sha1" => Md::sha1(), + "sha2_224" => Md::sha224(), + "sha2_256" => Md::sha256(), + "sha2_384" => Md::sha384(), + "sha2_512" => Md::sha512(), + _ => Md::sha256(), + }; + let rsa = Rsa::private_key_from_pem(pkey_input).unwrap(); + let pkey = PKey::from_rsa(rsa).unwrap(); + + let mut ctx = PkeyCtx::new(&pkey).unwrap(); + ctx.sign_init().unwrap(); + ctx.set_signature_md(digest_algo).unwrap(); + + ctx.sign_to_vec(data, signature).unwrap(); + debug!( + "Signature: {:?} hex::encode(): {:?}", + signature, + hex::encode(&signature) + ); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::HashMap; + use std::env; + use std::fs::read; + use std::panic; + + // 测试数据 + const TEST_DATA: &[u8] = b"Hello, world!"; + + #[tokio::test] + async fn test_attributes_do_sign_rsahash() { + let attributes = HashMap::from([(DIGEST_ALGO.to_string(), "sha2_256".to_string())]); + + let result = panic::catch_unwind(|| async { + let current_dir = env::current_dir().expect("get current dir failed"); + let signature_buf = read(current_dir.join("test_assets").join("private.pem")).unwrap(); + let mut signature = Vec::new(); + // 使用私钥进行签名 + let inner_result = + do_sign_rsahash(&signature_buf, TEST_DATA, &attributes, &mut signature); + print!("attribute_: {:?}\n", inner_result); + }); + assert!(result.is_ok()); + + // 验证签名的正确性(这部分需要额外的代码来验证签名,这里仅示例) + // 假设有一个验证签名的函数 verify_signature + // assert!(verify_signature(PRIVATE_KEY_PEM, TEST_DATA, &signature)); + } + + #[test] + fn test_attributes_pkey_hash_algo_methods() { + let attributes = HashMap::from([(DIGEST_ALGO.to_string(), "sha1".to_string())]); + + // 测试 get_hash_algo_from_attributes + let hash_algo = PkeyHashAlgo::get_hash_algo_from_attributes(&attributes); + assert_eq!(hash_algo, PkeyHashAlgo::Sha1); + + // 测试 get_digest_algo_from_attributes + let digest_algo = PkeyHashAlgo::get_digest_algo_from_attributes(&attributes); + assert_eq!(digest_algo.block_size(), 64); + + // 测试 to_u8 + let u8_value = hash_algo.to_u8(); + assert_eq!(u8_value, 2); // Sha1 对应的值是 2 + } +} diff --git a/src/util/key.rs b/src/util/key.rs index c087d73..713ab51 100644 --- a/src/util/key.rs +++ b/src/util/key.rs @@ -38,7 +38,7 @@ pub fn get_datakey_full_name( visibility: &Visibility, ) -> LibraryResult { let names: Vec<_> = name.split(':').collect(); - if visibility.to_owned() == Visibility::Public { + if *visibility == Visibility::Public { return if names.len() <= 1 { Ok(name.to_owned()) } else { -- Gitee From f629198481849dfca2d10b26fe5ff18ddbe23b65 Mon Sep 17 00:00:00 2001 From: jlcoo Date: Tue, 2 Jul 2024 15:23:01 +0800 Subject: [PATCH 3/3] fix comments --- src/client/file_handler/ima.rs | 2 +- src/infra/sign_plugin/x509.rs | 2 +- src/util/attributes.rs | 6 +----- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/src/client/file_handler/ima.rs b/src/client/file_handler/ima.rs index cc24821..1ccdd44 100644 --- a/src/client/file_handler/ima.rs +++ b/src/client/file_handler/ima.rs @@ -1,6 +1,6 @@ /* * - * * // Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. + * * // Copyright (c) 2024 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 diff --git a/src/infra/sign_plugin/x509.rs b/src/infra/sign_plugin/x509.rs index 5acdc60..a497e6e 100644 --- a/src/infra/sign_plugin/x509.rs +++ b/src/infra/sign_plugin/x509.rs @@ -638,7 +638,7 @@ impl SignPlugins for X509Plugin { Ok(cms_signature.to_der()?) } SignType::RsaHash => { - // https://github.com/sfackler/rust-openssl/blob/1b4c9b0e47aaefb8fb512d97d68a091b4f624812/openssl/src/pkey_ctx.rs#L863 + // rust-openssl/openssl/src/pkey_ctx.rs let mut signature = vec![]; attributes::do_sign_rsahash( self.private_key.unsecure(), diff --git a/src/util/attributes.rs b/src/util/attributes.rs index 0b1749f..35c34a4 100644 --- a/src/util/attributes.rs +++ b/src/util/attributes.rs @@ -1,6 +1,6 @@ /* * - * * // Copyright (c) 2023 Huawei Technologies Co.,Ltd. All rights reserved. + * * // Copyright (c) 2024 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 @@ -150,10 +150,6 @@ mod tests { print!("attribute_: {:?}\n", inner_result); }); assert!(result.is_ok()); - - // 验证签名的正确性(这部分需要额外的代码来验证签名,这里仅示例) - // 假设有一个验证签名的函数 verify_signature - // assert!(verify_signature(PRIVATE_KEY_PEM, TEST_DATA, &signature)); } #[test] -- Gitee