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{:?}",