Skip to content

Commit

Permalink
feat: upload c2c image & stream sha1
Browse files Browse the repository at this point in the history
  • Loading branch information
pk5ls20 committed Feb 19, 2025
1 parent 5af3ee2 commit 25ff9d9
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 17 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions mania/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ quick-xml = { version = "0.37.2", features = ["serialize"] }
regex = "1.11.1"
tokio-util = "0.7.13"
sha1 = "0.11.0-pre.4"
digest = "0.11.0-pre.9"

[build-dependencies]
prost-build = "0.13.4"
Expand Down
1 change: 1 addition & 0 deletions mania/src/core/crypto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use md5::{Digest, Md5};
use p256::{EncodedPoint, PublicKey, ecdh::EphemeralSecret};

pub mod consts;
mod stream_sha1;
pub mod tea;

/// The original macro that @wybxc originally wrote (b81f75b7) was perfect.
Expand Down
129 changes: 129 additions & 0 deletions mania/src/core/crypto/stream_sha1.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use core::{mem, slice::from_ref};
use digest::{
array::Array,
block_buffer::{BlockBuffer, Eager},
typenum::{U64, Unsigned},
};
use sha1::compress;

#[derive(Default)]
struct StreamSha1Core {
h: [u32; 5] = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0],
block_len: u64 = 0,
buffer: BlockBuffer<U64, Eager>,
}

impl StreamSha1Core {
#[inline]
fn hash(&self) -> [u8; 20] {
let mut digest = [0u8; 20];
for (i, &value) in self.h.iter().enumerate() {
digest[i * 4..(i + 1) * 4].copy_from_slice(&value.to_le_bytes());
}
digest
}

#[inline]
pub fn update(&mut self, input: &[u8]) {
self.buffer.digest_blocks(input, |blocks| {
self.block_len += blocks.len() as u64;
let blocks_core = Array::cast_slice_to_core(blocks);
compress(&mut self.h, blocks_core);
});
}

#[inline]
pub fn finalize(mut self) -> [u8; 20] {
let bs = U64::U64;
let bit_len = 8 * (self.buffer.get_pos() as u64 + bs * self.block_len);
let mut h = self.h;
self.buffer
.len64_padding_be(bit_len, |b| compress(&mut h, from_ref(&b.0)));
let mut out = [0u8; 20];
for (chunk, v) in out.chunks_exact_mut(4).zip(h.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
out
}
}

#[derive(Default)]
pub struct StreamSha1 {
hasher: StreamSha1Core,
block_size: usize = 1024 * 1024,
offset: usize = 0,
digests_stream: Vec<[u8; 20]>,
}

impl StreamSha1 {
pub fn new() -> Self {
Self::default()
}

pub fn new_with_size(block_size: usize) -> Self {
Self {
hasher: StreamSha1Core::default(),
block_size,
offset: 0,
digests_stream: Vec::new(),
}
}

pub fn update(&mut self, data: &[u8]) {
if data.len() + self.offset < self.block_size {
self.hasher.update(data);
self.offset += data.len();
} else {
let range = self.block_size - self.offset;
let block = &data[..range];
self.hasher.update(block);
let hash = self.hasher.hash();
self.digests_stream.push(hash);
self.offset = 0;
self.update(&data[range..]);
}
}

pub fn finalize(mut self) -> Vec<[u8; 20]> {
let final_digest = self.hasher.finalize();
self.digests_stream.push(final_digest);
mem::take(&mut self.digests_stream)
}
}

#[cfg(test)]
mod tests {
use super::*;

const EXPECTED_DIGESTS: [u8; 300] = [
0x15, 0x97, 0xb2, 0x42, 0x22, 0x2f, 0x46, 0x3d, 0xe7, 0xbd, 0xf4, 0xdf, 0x75, 0xd9, 0xdb,
0xca, 0xd4, 0xc9, 0x52, 0x33, 0x41, 0x5f, 0xbb, 0x1f, 0x1f, 0x65, 0xaf, 0xee, 0x93, 0x53,
0x31, 0x2a, 0xdb, 0x43, 0x53, 0xdd, 0x2b, 0x69, 0x8c, 0x02, 0x9d, 0x9d, 0x05, 0x86, 0x70,
0xc3, 0x5d, 0x68, 0x00, 0xe9, 0x2d, 0xbf, 0xce, 0x6d, 0xd8, 0xb1, 0x1e, 0xfe, 0x80, 0x0f,
0x73, 0x71, 0x8b, 0x0b, 0x61, 0x4d, 0xbb, 0x1a, 0xef, 0xcb, 0x47, 0x82, 0x8e, 0x78, 0x73,
0x2b, 0xe0, 0x61, 0xb0, 0xca, 0x0f, 0x77, 0xd1, 0x0a, 0x70, 0xa9, 0xc0, 0x4e, 0xd8, 0x35,
0xff, 0x45, 0x93, 0x98, 0x13, 0xfd, 0x39, 0xce, 0xee, 0x02, 0x4f, 0x9e, 0x2e, 0xc2, 0xc5,
0x78, 0x2b, 0x5c, 0x79, 0x5c, 0xd2, 0xd8, 0xd3, 0xe3, 0xc3, 0xe4, 0x1e, 0x99, 0xf3, 0xce,
0x81, 0xfd, 0xb4, 0x00, 0x3d, 0x89, 0x8a, 0x23, 0x01, 0xac, 0xee, 0xce, 0x13, 0x24, 0xe7,
0x02, 0x1d, 0x53, 0x0e, 0x0a, 0x82, 0xb9, 0xaf, 0xb8, 0xcf, 0x40, 0xa1, 0xc6, 0x48, 0x43,
0xb0, 0x01, 0xcb, 0xc9, 0x66, 0xd6, 0x18, 0x90, 0x39, 0x1a, 0xc5, 0xa0, 0xe2, 0xf3, 0xca,
0x9d, 0x90, 0xfc, 0xa9, 0xe3, 0xd8, 0xc5, 0x2b, 0x64, 0xe1, 0xac, 0xc0, 0x19, 0x64, 0x0f,
0x98, 0xeb, 0x7a, 0xbc, 0x36, 0x50, 0xa1, 0xab, 0x09, 0xb7, 0xc9, 0x2d, 0x9f, 0x82, 0xb9,
0x8c, 0x6a, 0xe5, 0x89, 0x6d, 0x35, 0x1d, 0xe4, 0x38, 0xe0, 0x38, 0xdc, 0x7a, 0x73, 0x46,
0xd1, 0xf9, 0xbf, 0x20, 0x76, 0x01, 0x77, 0xe4, 0x8b, 0x1d, 0x0b, 0x39, 0xed, 0x63, 0x76,
0x1c, 0x8b, 0x81, 0x1d, 0x02, 0xd0, 0x02, 0x23, 0x71, 0x51, 0x8b, 0x4a, 0x11, 0xe1, 0x5e,
0x73, 0xdb, 0xa7, 0x5f, 0xee, 0x54, 0x74, 0xb5, 0x59, 0x8d, 0x9f, 0xfe, 0x41, 0x79, 0xb1,
0xbf, 0xe3, 0x1f, 0xaf, 0x85, 0x03, 0xde, 0x3b, 0x16, 0x29, 0xee, 0xe7, 0x2e, 0x35, 0xa2,
0x46, 0xb7, 0x15, 0x58, 0xf9, 0x8f, 0x87, 0xa0, 0xfa, 0x1b, 0x59, 0x23, 0x63, 0x94, 0xfc,
0xd0, 0x64, 0x11, 0x38, 0x68, 0xd6, 0x84, 0x09, 0x16, 0x8a, 0x66, 0xad, 0xb1, 0x1d, 0x19,
];

#[test]
fn test_stream_sha1() {
let data = b"qwq".repeat(2233 * 2233);
let mut hasher = StreamSha1::new();
hasher.update(&data);
let digest = hasher.finalize().concat();
assert_eq!(digest, EXPECTED_DIGESTS);
}
}
126 changes: 126 additions & 0 deletions mania/src/core/event/message/image_c2c_upload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use crate::core::event::prelude::*;
use crate::core::protos::message::NotOnlineImage;
use crate::core::protos::service::oidb::{
BytesPbReserveC2c, C2cUserInfo, ClientMeta, CommonHead, ExtBizInfo, FileInfo, FileType, IPv4,
MsgInfo, MultiMediaReqHead, Ntv2RichMediaReq, Ntv2RichMediaResp, PicExtBizInfo, PttExtBizInfo,
SceneInfo, UploadInfo, UploadReq, VideoExtBizInfo,
};
use crate::utility::random_gen::RandomGenerator;

#[derive(Debug, Default)]
pub struct ImageC2CUploadArgs {
pub uid: String,
pub size: u32,
pub md5: Bytes,
pub sha1: Bytes,
pub name: String,
pub pic_type: u32,
pub sub_type: u32,
pub height: u32,
pub width: u32,
pub summary: String,
}

#[derive(Debug, Default)]
pub struct ImageC2CUploadRes {
pub msg_info: MsgInfo,
pub not_online_image: NotOnlineImage,
pub u_key: Option<String>,
pub ipv4s: Vec<IPv4>,
}

#[oidb_command(0x11c5, 100)]
#[derive(Debug, ServerEvent, Default)]
pub struct ImageC2CUploadEvent {
pub req: ImageC2CUploadArgs,
pub res: ImageC2CUploadRes,
}

impl ClientEvent for ImageC2CUploadEvent {
fn build(&self, _: &Context) -> CEBuildResult {
let req = dda!(Ntv2RichMediaReq {
req_head: Some(MultiMediaReqHead {
common: Some(CommonHead {
request_id: 1,
command: 100,
}),
scene: Some(dda!(SceneInfo {
request_type: 2,
business_type: 1,
scene_type: 1,
c2c: Some(C2cUserInfo {
account_type: 2,
target_uid: self.req.uid.clone(),
})
})),
client: Some(ClientMeta { agent_type: 2 }),
}),
upload: Some(UploadReq {
upload_info: vec![UploadInfo {
file_info: Some(FileInfo {
file_size: self.req.size,
file_hash: hex::encode(self.req.md5.clone()),
file_sha1: hex::encode(self.req.sha1.clone()),
file_name: self.req.name.to_owned(),
r#type: Some(FileType {
r#type: 1,
pic_format: self.req.pic_type,
video_format: 0,
voice_format: 0,
}),
width: self.req.width,
height: self.req.height,
time: 0,
original: 1,
}),
sub_file_type: 0,
}],
try_fast_upload_completed: true,
srv_send_msg: false,
client_random_id: RandomGenerator::rand_u64(),
compat_q_msg_scene_type: 2,
ext_biz_info: Some(dda!(ExtBizInfo {
pic: Some(dda!(PicExtBizInfo {
biz_type: self.req.sub_type,
text_summary: self.req.summary.clone(),
bytes_pb_reserve_c2c: Some(dda!(BytesPbReserveC2c {
sub_type: self.req.sub_type,
field8: self.req.summary.clone(),
})),
})),
video: Some(dda!(VideoExtBizInfo {
bytes_pb_reserve: vec![],
})),
ptt: Some(dda!(PttExtBizInfo {
bytes_reserve: vec![],
bytes_pb_reserve: vec![],
bytes_general_flags: vec![],
})),
})),
client_seq: 0,
no_need_compat_msg: false,
}),
});
Ok(OidbPacket::new(0x11c5, 100, req.encode_to_vec(), false, true).to_binary())
}

fn parse(packet: Bytes, _: &Context) -> CEParseResult {
let resp = OidbPacket::parse_into::<Ntv2RichMediaResp>(packet)?;
let upload = resp
.upload
.ok_or_else(|| EventError::OtherError("Missing UploadResp".to_string()))?;
let msg_info = upload
.msg_info
.ok_or_else(|| EventError::OtherError("Missing MsgInfo".to_string()))?;
let not_online_image = NotOnlineImage::decode(Bytes::from(upload.compat_q_msg))?;
let ipv4s = upload.i_pv4s;
Ok(ClientResult::single(Box::new(dda!(Self {
res: ImageC2CUploadRes {
msg_info,
not_online_image,
ipv4s,
u_key: upload.u_key,
}
}))))
}
}
1 change: 1 addition & 0 deletions mania/src/core/event/message/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod file_c2c_download;
pub mod file_group_download;
pub mod image_c2c_download;
pub mod image_c2c_upload;
pub mod image_group_download;
pub mod image_group_upload;
pub mod multi_msg_download;
Expand Down
Loading

0 comments on commit 25ff9d9

Please sign in to comment.