wechat_protocol/calls/
voice_offer.rs1use super::client::VoiceParameters;
6use crate::{crypto, protobuf as p};
7use anyhow::Result;
8use zeroize::Zeroizing;
9
10pub struct VoiceOffer {
14 sdk: Vec<u8>,
15 engine: Vec<u8>,
16 channel_internal: Zeroizing<[u8; 12]>,
17 channel_advertised: [u8; 12],
18}
19impl VoiceOffer {
20 pub fn from_owned_random(owner_version: i32, random32: [u8; 32]) -> Result<Self> {
24 let random32 = Zeroizing::new(random32);
25 let digest = Zeroizing::new(crypto::md5(random32.as_ref()));
26 let mut internal = Zeroizing::new([0; 12]);
27 internal.copy_from_slice(&digest[..12]);
28 let mut advertised = *internal;
29 advertised[..4].reverse();
30 advertised[4..8].reverse();
31 advertised[8..10].reverse();
32 advertised[10..12].reverse();
33 let mut engine = Vec::new();
34 for field in (1..=27).chain(36..=40) {
35 if field == 27 {
36 engine.extend(p::int32(field, owner_version));
37 } else {
38 let value = match field {
39 1 => 0x457,
40 5 => 0x40000,
41 6 => 0x100,
42 25 => 5,
43 26 => 2,
44 _ => 0,
45 };
46 engine.extend(p::uint(field, value));
47 }
48 }
49 let sdk = p::message(&[
50 p::uint(1, 0x050000cb),
51 p::bytes(2, format!("0x{:x}", owner_version as u32).as_bytes()),
52 ])?;
53 Ok(Self {
54 sdk,
55 engine,
56 channel_internal: internal,
57 channel_advertised: advertised,
58 })
59 }
60 pub fn sdk_info(&self) -> &[u8] {
61 &self.sdk
62 }
63 pub fn engine_advertisement(&self) -> &[u8] {
64 &self.engine
65 }
66 pub fn advertised_channel_identity(&self) -> &[u8; 12] {
67 &self.channel_advertised
68 }
69 pub fn internal_channel_identity(&self) -> &[u8; 12] {
70 &self.channel_internal
71 }
72 pub fn apply_to(&self, parameters: &mut VoiceParameters) {
75 parameters.sdk = self.sdk.clone();
76 parameters.engine_capability = self.engine.clone();
77 parameters.channel_capability = self.channel_advertised.to_vec();
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 #[test]
85 fn request_advertises_a_mask_and_fresh_channel_identity_not_selected_buffers() {
86 let seed = std::array::from_fn(|i| i as u8);
87 let offer = VoiceOffer::from_owned_random(0x01020300, seed).unwrap();
88 let fields = p::decode(offer.engine_advertisement()).unwrap();
89 assert_eq!(p::scalar(&fields, 1).unwrap(), 0x457);
90 assert_eq!(p::scalar(&fields, 5).unwrap(), 0x40000);
91 assert_ne!(p::scalar(&fields, 5).unwrap(), 17);
92 assert_eq!(p::scalar(&fields, 6).unwrap(), 0x100);
93 assert_eq!(p::scalar(&fields, 7).unwrap(), 0);
94 assert_eq!(p::scalar(&fields, 25).unwrap(), 5);
95 assert_eq!(p::scalar(&fields, 26).unwrap(), 2);
96 assert_eq!(p::scalar(&fields, 27).unwrap(), 0x01020300);
97 assert_eq!(fields.len(), 32);
98 assert_eq!(
99 hex::encode(offer.internal_channel_identity()),
100 "b4ffcb23737cec315a4a4d1a"
101 );
102 assert_eq!(
103 hex::encode(offer.advertised_channel_identity()),
104 "23cbffb431ec7c734a5a1a4d"
105 );
106 assert_ne!(
107 offer.advertised_channel_identity().as_slice(),
108 p::message(&[p::uint(1, 2), p::uint(2, 3)]).unwrap()
109 );
110 let next = VoiceOffer::from_owned_random(0x01020300, [7; 32]).unwrap();
111 assert_ne!(
112 next.advertised_channel_identity(),
113 offer.advertised_channel_identity()
114 );
115 let signed = VoiceOffer::from_owned_random(i32::MIN, [1; 32]).unwrap();
116 assert_eq!(
117 p::scalar(&p::decode(signed.engine_advertisement()).unwrap(), 27).unwrap() as i64,
118 i64::from(i32::MIN)
119 );
120 assert_eq!(
121 p::data(&p::decode(signed.sdk_info()).unwrap(), 2).unwrap(),
122 b"0x80000000"
123 );
124 }
125}