diff --git a/src/handles/robot.rs b/src/handles/robot.rs index 071f674..60bb519 100644 --- a/src/handles/robot.rs +++ b/src/handles/robot.rs @@ -298,3 +298,90 @@ async fn dispatch_robot_message( command_runtime::run(runtime).await } + +#[cfg(test)] +mod tests { + + use super::super::super::wxwork_robot::base64; + use super::super::super::wxwork_robot::message; + use super::super::super::wxwork_robot::project::WXWorkProject; + + const WXWORKROBOT_TEST_MSG_ORIGIN: &[u8] = b""; + const WXWORKROBOT_TEST_MSG_REPLY: &str = ""; + + #[test] + fn project_decode_and_verify() { + let encrypt_msg_b64_res = + message::get_msg_encrypt_from_bytes(bytes::Bytes::from(WXWORKROBOT_TEST_MSG_ORIGIN)); + assert!(encrypt_msg_b64_res.is_some()); + + let json_value = serde_json::from_str("{ \"name\": \"test_proj\", \"token\": \"hJqcu3uJ9Tn2gXPmxx2w9kkCkCE2EPYo\", \"encodingAESKey\": \"6qkdMrq68nTKduznJYO1A37W2oEgpkMUvkttRToqhUt\", \"cmds\": {} }").unwrap(); + let proj_obj_res = WXWorkProject::new(&json_value); + assert!(proj_obj_res.is_some()); + if !proj_obj_res.is_some() { + return; + } + let proj_obj = proj_obj_res.unwrap(); + + let msg_dec: message::WXWorkMessageDec; + if let Some(encrypt_msg_b64) = encrypt_msg_b64_res { + assert!(proj_obj.check_msg_signature( + "8fa1a2c27ee20431b1f781600d0af971db3cc12b", + "1592905675", + "da455e270d961d94", + encrypt_msg_b64.as_str() + )); + + let msg_dec_res = proj_obj.decrypt_msg_raw_base64_content(encrypt_msg_b64.as_str()); + assert!(msg_dec_res.is_ok()); + msg_dec = if let Ok(x) = msg_dec_res { + x + } else { + return; + }; + } else { + return; + } + + // 提取数据 + let msg_ntf_res = message::get_msg_from_str(msg_dec.content.as_str()); + assert!(msg_ntf_res.is_some()); + let msg_ntf = if let Some(x) = msg_ntf_res { + x + } else { + return; + }; + + assert_eq!(msg_ntf.content, "@测试机器人 说啦啦啦热热热"); + } + + #[test] + fn project_encode_reply() { + let json_value = serde_json::from_str("{ \"name\": \"test_proj\", \"token\": \"hJqcu3uJ9Tn2gXPmxx2w9kkCkCE2EPYo\", \"encodingAESKey\": \"6qkdMrq68nTKduznJYO1A37W2oEgpkMUvkttRToqhUt\", \"cmds\": {} }").unwrap(); + let proj_obj_res = WXWorkProject::new(&json_value); + assert!(proj_obj_res.is_some()); + if !proj_obj_res.is_some() { + return; + } + let proj_obj = proj_obj_res.unwrap(); + + let random_str = String::from("5377875643139089"); + let encrypted_res = + proj_obj.encrypt_msg_raw(&WXWORKROBOT_TEST_MSG_REPLY.as_bytes(), &random_str); + assert!(encrypted_res.is_ok()); + + let encrypted_base64 = if let Ok(x) = encrypted_res { + match base64::STANDARD.encode(&x) { + Ok(v) => v, + Err(_) => { + assert!(false); + return; + } + } + } else { + return; + }; + + assert_eq!(encrypted_base64, "i84WNcyej8+Vo0tCZHLxCWt3ObZ2mvzs0cIGXLleX43mjd+TK1SYqdUOuPMS32ZJK0QyAq+Y6eVwqObEjrLTxGnlEeMOH2/f1CMxcPiRXUOTzOP4/qyeYI+PF9wAuJIajfJMHZCUiUSjS5cs18AS3XnO3VoP1hnGkMkxNy3CBFqQzgVkGsHhz3cQK94tzlkPWsveB8qQZjOJWxHst2Y+8Q=="); + } +} diff --git a/src/wxwork_robot/message.rs b/src/wxwork_robot/message.rs index 06b2328..e456438 100644 --- a/src/wxwork_robot/message.rs +++ b/src/wxwork_robot/message.rs @@ -543,7 +543,7 @@ pub fn pack_text_message(msg: WXWorkMessageTextRsp) -> Result { if let Ok(_) = writer.write_event(Event::Start(BytesStart::borrowed_name(b"xml"))) { if let Ok(_) = writer.write_event(Event::Start(BytesStart::borrowed_name(b"MsgType"))) { - let _ = writer.write_event(Event::CData(BytesText::from_plain_str("text"))); + let _ = writer.write_event(Event::Text(BytesText::from_plain_str("text"))); let _ = writer.write_event(Event::End(BytesEnd::borrowed(b"MsgType"))); } @@ -603,7 +603,7 @@ pub fn pack_markdown_message(msg: WXWorkMessageMarkdownRsp) -> Result; +type Aes256CbcNoPadding = Cbc; // #[derive(Clone)] struct WXWorkProjectCipherInfo { - pub cipher: Aes256CbcPkcs7, + pub cipher: Aes256CbcNoPadding, pub key: Vec, pub iv: Vec, } @@ -200,14 +200,14 @@ impl WXWorkProject { //let cipher_iv_len = ::BlockSize::to_usize(); //let cipher_iv_len = U16::to_usize(); // According to https://en.wikipedia.org/wiki/Block_size_(cryptography) - // Block size of AES is always 128bits + // Block size of AES is always 128bits/16bytes let cipher_iv_len: usize = 16; let cipher_iv = if aes_key_bin.len() >= cipher_iv_len { Vec::from(&aes_key_bin[0..cipher_iv_len]) } else { Vec::new() }; - let cipher_ctx = match Aes256CbcPkcs7::new_var(&aes_key_bin, &cipher_iv) { + let cipher_ctx = match Aes256CbcNoPadding::new_var(&aes_key_bin, &cipher_iv) { Ok(x) => x, Err(e) => { let err_msg = format!( @@ -363,7 +363,7 @@ impl WXWorkProject { match self.cipher_info.lock() { Ok(c) => { let ci = &*c; - decrypter = match Aes256CbcPkcs7::new_var(&ci.key, &ci.iv) { + decrypter = match Aes256CbcNoPadding::new_var(&ci.key, &ci.iv) { Ok(x) => x, Err(e) => { let ret = format!( @@ -463,7 +463,7 @@ impl WXWorkProject { &self, input: &str, ) -> Result { - let dec_bin_unpadding = match self.decrypt_msg_raw_base64(input) { + let dec_bin = match self.decrypt_msg_raw_base64(input) { Ok(x) => x, Err(e) => { return Err(e); @@ -475,7 +475,7 @@ impl WXWorkProject { self.name(), input ); - // let dec_bin_unpadding = self.pkcs7_decode(&dec_bin); + let dec_bin_unpadding = self.pkcs7_decode(&dec_bin); if dec_bin_unpadding.len() <= 20 { let err_msg = format!( @@ -544,7 +544,7 @@ impl WXWorkProject { }) } - pub fn encrypt_msg_raw(&self, input: &[u8]) -> Result, String> { + pub fn encrypt_msg_raw(&self, input: &[u8], random_str: &String) -> Result, String> { // rand_msg=AES_Decrypt(aes_msg) // 去掉rand_msg头部的16个随机字节和4个字节的msg_len,截取msg_len长度的部分即为msg,剩下的为尾部的receiveid // 网络字节序,回包的receiveid直接为空即可 @@ -554,11 +554,11 @@ impl WXWorkProject { let mut padded_plaintext: Vec = Vec::new(); padded_plaintext.reserve(64 + input.len()); - padded_plaintext.extend_from_slice(self.alloc_random_str().as_bytes()); + padded_plaintext.extend_from_slice(random_str.as_bytes()); padded_plaintext.extend_from_slice(&input_len_buf); padded_plaintext.extend_from_slice(input); - // let padded_input = self.pkcs7_encode(&padded_plaintext); + let padded_input = self.pkcs7_encode(&padded_plaintext); let encrypter; // let block_size: usize; @@ -566,7 +566,7 @@ impl WXWorkProject { match self.cipher_info.lock() { Ok(c) => { let ci = &*c; - encrypter = match Aes256CbcPkcs7::new_var(&ci.key, &ci.iv) { + encrypter = match Aes256CbcNoPadding::new_var(&ci.key, &ci.iv) { Ok(x) => x, Err(e) => { let ret = format!( @@ -609,7 +609,8 @@ impl WXWorkProject { } } - Ok(encrypter.encrypt_vec(&padded_plaintext)) + let ret = encrypter.encrypt_vec(&padded_input); + Ok(ret) /* encrypter.pad(false); @@ -649,9 +650,22 @@ impl WXWorkProject { pub fn encrypt_msg_raw_base64(&self, input: &[u8]) -> Result { // msg_encrypt = Base64_Encode(AES_Encrypt(rand_msg)) - match self.encrypt_msg_raw(&input) { + let random_str = self.alloc_random_str(); + match self.encrypt_msg_raw(&input, &random_str) { Ok(x) => match base64::STANDARD.encode(&x) { - Ok(v) => Ok(v), + Ok(v) => { + debug!( + "project \"{}\" use random string {} and encrypt \"{}\" to {}", + self.name(), + random_str, + match String::from_utf8(input.to_vec()) { + Ok(y) => y, + Err(_) => hex::encode(input), + }, + v + ); + Ok(v) + } Err(e) => { let ret = format!( "project \"{}\" encrypt {} and encode to base64 failed, \n{:?}",