xref: /aosp_15_r20/system/authgraph/wire/src/fragmentation.rs (revision 4185b0660fbe514985fdcf75410317caad8afad1)
1*4185b066SAndroid Build Coastguard Worker // Copyright 2024 Google LLC
2*4185b066SAndroid Build Coastguard Worker //
3*4185b066SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*4185b066SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*4185b066SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*4185b066SAndroid Build Coastguard Worker //
7*4185b066SAndroid Build Coastguard Worker //      http://www.apache.org/licenses/LICENSE-2.0
8*4185b066SAndroid Build Coastguard Worker //
9*4185b066SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*4185b066SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*4185b066SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*4185b066SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*4185b066SAndroid Build Coastguard Worker // limitations under the License.
14*4185b066SAndroid Build Coastguard Worker 
15*4185b066SAndroid Build Coastguard Worker //! Helpers for message fragmentation and reassembly.
16*4185b066SAndroid Build Coastguard Worker 
17*4185b066SAndroid Build Coastguard Worker use alloc::borrow::Cow;
18*4185b066SAndroid Build Coastguard Worker use alloc::vec::Vec;
19*4185b066SAndroid Build Coastguard Worker 
20*4185b066SAndroid Build Coastguard Worker /// Prefix byte indicating more data is to come.
21*4185b066SAndroid Build Coastguard Worker const PREFIX_MORE_TO_COME: u8 = 0xcc; // 'ccontinues'
22*4185b066SAndroid Build Coastguard Worker /// Prefix byte indicating that this is the final fragment.
23*4185b066SAndroid Build Coastguard Worker const PREFIX_FINAL_FRAGMENT: u8 = 0xdd; // 'ddone'
24*4185b066SAndroid Build Coastguard Worker 
25*4185b066SAndroid Build Coastguard Worker /// Empty placeholder message indicating more data is due.
26*4185b066SAndroid Build Coastguard Worker pub const PLACEHOLDER_MORE_TO_COME: &[u8] = &[PREFIX_MORE_TO_COME];
27*4185b066SAndroid Build Coastguard Worker 
28*4185b066SAndroid Build Coastguard Worker /// Helper to emit a single message in fragments.
29*4185b066SAndroid Build Coastguard Worker pub struct Fragmenter<'a> {
30*4185b066SAndroid Build Coastguard Worker     data: &'a [u8],
31*4185b066SAndroid Build Coastguard Worker     max_size: usize,
32*4185b066SAndroid Build Coastguard Worker }
33*4185b066SAndroid Build Coastguard Worker 
34*4185b066SAndroid Build Coastguard Worker impl<'a> Fragmenter<'a> {
35*4185b066SAndroid Build Coastguard Worker     /// Create a fragmentation iterator for the given data.
new(data: &'a [u8], max_size: usize) -> Fragmenter<'a>36*4185b066SAndroid Build Coastguard Worker     pub fn new(data: &'a [u8], max_size: usize) -> Fragmenter<'a> {
37*4185b066SAndroid Build Coastguard Worker         assert!(max_size > 1);
38*4185b066SAndroid Build Coastguard Worker         Self { data, max_size }
39*4185b066SAndroid Build Coastguard Worker     }
40*4185b066SAndroid Build Coastguard Worker }
41*4185b066SAndroid Build Coastguard Worker 
42*4185b066SAndroid Build Coastguard Worker impl<'a> Iterator for Fragmenter<'a> {
43*4185b066SAndroid Build Coastguard Worker     type Item = Vec<u8>;
next(&mut self) -> Option<Self::Item>44*4185b066SAndroid Build Coastguard Worker     fn next(&mut self) -> Option<Self::Item> {
45*4185b066SAndroid Build Coastguard Worker         if self.data.is_empty() {
46*4185b066SAndroid Build Coastguard Worker             None
47*4185b066SAndroid Build Coastguard Worker         } else {
48*4185b066SAndroid Build Coastguard Worker             let consume = core::cmp::min(self.max_size - 1, self.data.len());
49*4185b066SAndroid Build Coastguard Worker             let marker =
50*4185b066SAndroid Build Coastguard Worker                 if consume < self.data.len() { PREFIX_MORE_TO_COME } else { PREFIX_FINAL_FRAGMENT };
51*4185b066SAndroid Build Coastguard Worker             let mut result = Vec::with_capacity(consume + 1);
52*4185b066SAndroid Build Coastguard Worker             result.push(marker);
53*4185b066SAndroid Build Coastguard Worker             result.extend_from_slice(&self.data[..consume]);
54*4185b066SAndroid Build Coastguard Worker             self.data = &self.data[consume..];
55*4185b066SAndroid Build Coastguard Worker             Some(result)
56*4185b066SAndroid Build Coastguard Worker         }
57*4185b066SAndroid Build Coastguard Worker     }
58*4185b066SAndroid Build Coastguard Worker }
59*4185b066SAndroid Build Coastguard Worker 
60*4185b066SAndroid Build Coastguard Worker /// Buffer to accumulate fragmented messages.
61*4185b066SAndroid Build Coastguard Worker #[derive(Default)]
62*4185b066SAndroid Build Coastguard Worker pub struct Reassembler(Vec<u8>);
63*4185b066SAndroid Build Coastguard Worker 
64*4185b066SAndroid Build Coastguard Worker impl Reassembler {
65*4185b066SAndroid Build Coastguard Worker     /// Accumulate message data, possibly resulting in a complete message.
accumulate<'a>(&mut self, frag: &'a [u8]) -> Option<Cow<'a, [u8]>>66*4185b066SAndroid Build Coastguard Worker     pub fn accumulate<'a>(&mut self, frag: &'a [u8]) -> Option<Cow<'a, [u8]>> {
67*4185b066SAndroid Build Coastguard Worker         let (more, content) = Self::split_msg(frag);
68*4185b066SAndroid Build Coastguard Worker         if more {
69*4185b066SAndroid Build Coastguard Worker             // More to come, so accumulate this data and return empty response.
70*4185b066SAndroid Build Coastguard Worker             self.0.extend_from_slice(content);
71*4185b066SAndroid Build Coastguard Worker             None
72*4185b066SAndroid Build Coastguard Worker         } else if self.0.is_empty() {
73*4185b066SAndroid Build Coastguard Worker             // For shorter messages (the mainline case) we can directly pass through the single
74*4185b066SAndroid Build Coastguard Worker             // message's content.
75*4185b066SAndroid Build Coastguard Worker             Some(Cow::Borrowed(content))
76*4185b066SAndroid Build Coastguard Worker         } else {
77*4185b066SAndroid Build Coastguard Worker             // Process the accumulated full request as an owned vector
78*4185b066SAndroid Build Coastguard Worker             let mut full_req = core::mem::take(&mut self.0);
79*4185b066SAndroid Build Coastguard Worker             full_req.extend_from_slice(content);
80*4185b066SAndroid Build Coastguard Worker             Some(Cow::Owned(full_req))
81*4185b066SAndroid Build Coastguard Worker         }
82*4185b066SAndroid Build Coastguard Worker     }
83*4185b066SAndroid Build Coastguard Worker 
84*4185b066SAndroid Build Coastguard Worker     /// Split a message into an indication of whether more data is to come, and the content.
85*4185b066SAndroid Build Coastguard Worker     ///
86*4185b066SAndroid Build Coastguard Worker     /// # Panics
87*4185b066SAndroid Build Coastguard Worker     ///
88*4185b066SAndroid Build Coastguard Worker     /// This function panics if the provided message fragment has an unexpected message prefix.
split_msg(data: &[u8]) -> (bool, &[u8])89*4185b066SAndroid Build Coastguard Worker     fn split_msg(data: &[u8]) -> (bool, &[u8]) {
90*4185b066SAndroid Build Coastguard Worker         if data.is_empty() {
91*4185b066SAndroid Build Coastguard Worker             (false, data)
92*4185b066SAndroid Build Coastguard Worker         } else {
93*4185b066SAndroid Build Coastguard Worker             match data[0] {
94*4185b066SAndroid Build Coastguard Worker                 PREFIX_MORE_TO_COME => (true, &data[1..]),
95*4185b066SAndroid Build Coastguard Worker                 PREFIX_FINAL_FRAGMENT => (false, &data[1..]),
96*4185b066SAndroid Build Coastguard Worker                 _ => panic!("data fragment with incorrect prefix"),
97*4185b066SAndroid Build Coastguard Worker             }
98*4185b066SAndroid Build Coastguard Worker         }
99*4185b066SAndroid Build Coastguard Worker     }
100*4185b066SAndroid Build Coastguard Worker }
101*4185b066SAndroid Build Coastguard Worker 
102*4185b066SAndroid Build Coastguard Worker #[cfg(test)]
103*4185b066SAndroid Build Coastguard Worker mod tests {
104*4185b066SAndroid Build Coastguard Worker     use super::*;
105*4185b066SAndroid Build Coastguard Worker     use alloc::string::{String, ToString};
106*4185b066SAndroid Build Coastguard Worker     use alloc::vec;
107*4185b066SAndroid Build Coastguard Worker     use core::cell::RefCell;
108*4185b066SAndroid Build Coastguard Worker 
109*4185b066SAndroid Build Coastguard Worker     #[test]
test_fragmentation()110*4185b066SAndroid Build Coastguard Worker     fn test_fragmentation() {
111*4185b066SAndroid Build Coastguard Worker         let tests = [
112*4185b066SAndroid Build Coastguard Worker             (
113*4185b066SAndroid Build Coastguard Worker                 "a0a1a2a3a4a5a6a7a8a9",
114*4185b066SAndroid Build Coastguard Worker                 2,
115*4185b066SAndroid Build Coastguard Worker                 vec![
116*4185b066SAndroid Build Coastguard Worker                     "cca0", "cca1", "cca2", "cca3", "cca4", "cca5", "cca6", "cca7", "cca8", "dda9",
117*4185b066SAndroid Build Coastguard Worker                 ],
118*4185b066SAndroid Build Coastguard Worker             ),
119*4185b066SAndroid Build Coastguard Worker             ("a0a1a2a3a4a5a6a7a8a9", 5, vec!["cca0a1a2a3", "cca4a5a6a7", "dda8a9"]),
120*4185b066SAndroid Build Coastguard Worker             ("a0a1a2a3a4a5a6a7a8a9", 9, vec!["cca0a1a2a3a4a5a6a7", "dda8a9"]),
121*4185b066SAndroid Build Coastguard Worker             ("a0a1a2a3a4a5a6a7a8a9", 80, vec!["dda0a1a2a3a4a5a6a7a8a9"]),
122*4185b066SAndroid Build Coastguard Worker         ];
123*4185b066SAndroid Build Coastguard Worker         for (input, max_size, want) in &tests {
124*4185b066SAndroid Build Coastguard Worker             let data = hex::decode(input).unwrap();
125*4185b066SAndroid Build Coastguard Worker             let fragmenter = Fragmenter::new(&data, *max_size);
126*4185b066SAndroid Build Coastguard Worker             let got: Vec<String> = fragmenter.map(hex::encode).collect();
127*4185b066SAndroid Build Coastguard Worker             let want: Vec<String> = want.iter().map(|s| s.to_string()).collect();
128*4185b066SAndroid Build Coastguard Worker             assert_eq!(got, want, "for input {input} max_size {max_size}");
129*4185b066SAndroid Build Coastguard Worker         }
130*4185b066SAndroid Build Coastguard Worker     }
131*4185b066SAndroid Build Coastguard Worker 
132*4185b066SAndroid Build Coastguard Worker     #[test]
133*4185b066SAndroid Build Coastguard Worker     #[should_panic]
test_reassembly_wrong_prefix()134*4185b066SAndroid Build Coastguard Worker     fn test_reassembly_wrong_prefix() {
135*4185b066SAndroid Build Coastguard Worker         // Failure case: unexpected marker byte
136*4185b066SAndroid Build Coastguard Worker         let mut pending = Reassembler::default();
137*4185b066SAndroid Build Coastguard Worker         let _ = pending.accumulate(&[0x00, 0x01, 0x02, 0x03]);
138*4185b066SAndroid Build Coastguard Worker     }
139*4185b066SAndroid Build Coastguard Worker 
140*4185b066SAndroid Build Coastguard Worker     #[test]
test_reassembly()141*4185b066SAndroid Build Coastguard Worker     fn test_reassembly() {
142*4185b066SAndroid Build Coastguard Worker         let tests = [
143*4185b066SAndroid Build Coastguard Worker             // Single messages
144*4185b066SAndroid Build Coastguard Worker             (vec!["dd"], ""),
145*4185b066SAndroid Build Coastguard Worker             (vec!["dd0000"], "0000"),
146*4185b066SAndroid Build Coastguard Worker             (vec!["dd010203"], "010203"),
147*4185b066SAndroid Build Coastguard Worker             // Multipart messages.
148*4185b066SAndroid Build Coastguard Worker             (vec!["cc0102", "dd0304"], "01020304"),
149*4185b066SAndroid Build Coastguard Worker             (vec!["cc01", "cc02", "dd0304"], "01020304"),
150*4185b066SAndroid Build Coastguard Worker             (vec!["cc", "cc02", "dd0304"], "020304"),
151*4185b066SAndroid Build Coastguard Worker             // Failure case: empty message (no marker byte)
152*4185b066SAndroid Build Coastguard Worker             (vec![], ""),
153*4185b066SAndroid Build Coastguard Worker         ];
154*4185b066SAndroid Build Coastguard Worker         for (frags, want) in &tests {
155*4185b066SAndroid Build Coastguard Worker             let mut done = false;
156*4185b066SAndroid Build Coastguard Worker             let mut pending = Reassembler::default();
157*4185b066SAndroid Build Coastguard Worker             for frag in frags {
158*4185b066SAndroid Build Coastguard Worker                 assert!(!done, "left over fragments found");
159*4185b066SAndroid Build Coastguard Worker                 let frag = hex::decode(frag).unwrap();
160*4185b066SAndroid Build Coastguard Worker                 let result = pending.accumulate(&frag);
161*4185b066SAndroid Build Coastguard Worker                 if let Some(got) = result {
162*4185b066SAndroid Build Coastguard Worker                     assert_eq!(&hex::encode(got), want, "for input {frags:?}");
163*4185b066SAndroid Build Coastguard Worker                     done = true;
164*4185b066SAndroid Build Coastguard Worker                 }
165*4185b066SAndroid Build Coastguard Worker             }
166*4185b066SAndroid Build Coastguard Worker         }
167*4185b066SAndroid Build Coastguard Worker     }
168*4185b066SAndroid Build Coastguard Worker 
169*4185b066SAndroid Build Coastguard Worker     #[test]
test_fragmentation_reassembly()170*4185b066SAndroid Build Coastguard Worker     fn test_fragmentation_reassembly() {
171*4185b066SAndroid Build Coastguard Worker         let input = "a0a1a2a3a4a5a6a7a8a9b0b1b2b3b4b5b6b7b8b9c0c1c2c3c4c5c6c7c8c9";
172*4185b066SAndroid Build Coastguard Worker         let data = hex::decode(input).unwrap();
173*4185b066SAndroid Build Coastguard Worker         for max_size in 2..data.len() + 2 {
174*4185b066SAndroid Build Coastguard Worker             let fragmenter = Fragmenter::new(&data, max_size);
175*4185b066SAndroid Build Coastguard Worker             let mut done = false;
176*4185b066SAndroid Build Coastguard Worker             let mut pending = Reassembler::default();
177*4185b066SAndroid Build Coastguard Worker             for frag in fragmenter {
178*4185b066SAndroid Build Coastguard Worker                 assert!(!done, "left over fragments found");
179*4185b066SAndroid Build Coastguard Worker                 let result = pending.accumulate(&frag);
180*4185b066SAndroid Build Coastguard Worker                 if let Some(got) = result {
181*4185b066SAndroid Build Coastguard Worker                     assert_eq!(&hex::encode(got), input, "for max_size {max_size}");
182*4185b066SAndroid Build Coastguard Worker                     done = true;
183*4185b066SAndroid Build Coastguard Worker                 }
184*4185b066SAndroid Build Coastguard Worker             }
185*4185b066SAndroid Build Coastguard Worker             assert!(done);
186*4185b066SAndroid Build Coastguard Worker         }
187*4185b066SAndroid Build Coastguard Worker     }
188*4185b066SAndroid Build Coastguard Worker 
189*4185b066SAndroid Build Coastguard Worker     #[test]
test_ta_fragmentation_wrapper()190*4185b066SAndroid Build Coastguard Worker     fn test_ta_fragmentation_wrapper() {
191*4185b066SAndroid Build Coastguard Worker         // Simulate a `send()` standalone function for responses.
192*4185b066SAndroid Build Coastguard Worker         let rsp_reassembler = RefCell::new(Reassembler::default());
193*4185b066SAndroid Build Coastguard Worker         let full_rsp: RefCell<Option<Vec<u8>>> = RefCell::new(None);
194*4185b066SAndroid Build Coastguard Worker         let send = |data: &[u8]| {
195*4185b066SAndroid Build Coastguard Worker             if let Some(msg) = rsp_reassembler.borrow_mut().accumulate(data) {
196*4185b066SAndroid Build Coastguard Worker                 *full_rsp.borrow_mut() = Some(msg.to_vec());
197*4185b066SAndroid Build Coastguard Worker             }
198*4185b066SAndroid Build Coastguard Worker         };
199*4185b066SAndroid Build Coastguard Worker 
200*4185b066SAndroid Build Coastguard Worker         // Simulate a TA.
201*4185b066SAndroid Build Coastguard Worker         struct Ta {
202*4185b066SAndroid Build Coastguard Worker             pending_req: Reassembler,
203*4185b066SAndroid Build Coastguard Worker             max_size: usize,
204*4185b066SAndroid Build Coastguard Worker         }
205*4185b066SAndroid Build Coastguard Worker         impl Ta {
206*4185b066SAndroid Build Coastguard Worker             // Request fragment, response fragments emitted via `send`.
207*4185b066SAndroid Build Coastguard Worker             fn process_fragment<S: Fn(&[u8])>(&mut self, req_frag: &[u8], send: S) {
208*4185b066SAndroid Build Coastguard Worker                 // Accumulate request fragments until able to feed complete request to `process`.
209*4185b066SAndroid Build Coastguard Worker                 if let Some(full_req) = self.pending_req.accumulate(req_frag) {
210*4185b066SAndroid Build Coastguard Worker                     // Full request message is available, invoke the callback to get a full
211*4185b066SAndroid Build Coastguard Worker                     // response.
212*4185b066SAndroid Build Coastguard Worker                     let full_rsp = self.process(&full_req);
213*4185b066SAndroid Build Coastguard Worker                     for rsp_frag in Fragmenter::new(&full_rsp, self.max_size) {
214*4185b066SAndroid Build Coastguard Worker                         send(&rsp_frag);
215*4185b066SAndroid Build Coastguard Worker                     }
216*4185b066SAndroid Build Coastguard Worker                 }
217*4185b066SAndroid Build Coastguard Worker             }
218*4185b066SAndroid Build Coastguard Worker             // Full request to full response.
219*4185b066SAndroid Build Coastguard Worker             fn process(&self, req: &[u8]) -> Vec<u8> {
220*4185b066SAndroid Build Coastguard Worker                 // Simulate processing a request by echoing it back as a response.
221*4185b066SAndroid Build Coastguard Worker                 req.to_vec()
222*4185b066SAndroid Build Coastguard Worker             }
223*4185b066SAndroid Build Coastguard Worker         }
224*4185b066SAndroid Build Coastguard Worker 
225*4185b066SAndroid Build Coastguard Worker         let req = "a0a1a2a3a4a5a6a7a8a9b0b1b2b3b4b5b6b7b8b9c0c1c2c3c4c5c6c7c8c9";
226*4185b066SAndroid Build Coastguard Worker         let req_data = hex::decode(req).unwrap();
227*4185b066SAndroid Build Coastguard Worker         for max_size in 2..req_data.len() + 2 {
228*4185b066SAndroid Build Coastguard Worker             let mut ta = Ta { pending_req: Default::default(), max_size };
229*4185b066SAndroid Build Coastguard Worker             // Simulate multiple fragmented messages arriving at the TA.
230*4185b066SAndroid Build Coastguard Worker             for _msg_idx in 0..3 {
231*4185b066SAndroid Build Coastguard Worker                 // Reset the received-response buffer.
232*4185b066SAndroid Build Coastguard Worker                 *rsp_reassembler.borrow_mut() = Reassembler::default();
233*4185b066SAndroid Build Coastguard Worker                 *full_rsp.borrow_mut() = None;
234*4185b066SAndroid Build Coastguard Worker 
235*4185b066SAndroid Build Coastguard Worker                 for req_frag in Fragmenter::new(&req_data, max_size) {
236*4185b066SAndroid Build Coastguard Worker                     assert!(full_rsp.borrow().is_none(), "left over fragments found");
237*4185b066SAndroid Build Coastguard Worker                     ta.process_fragment(&req_frag, send);
238*4185b066SAndroid Build Coastguard Worker                 }
239*4185b066SAndroid Build Coastguard Worker                 // After all the request fragments have been sent in, expect to have a complete
240*4185b066SAndroid Build Coastguard Worker                 // response.
241*4185b066SAndroid Build Coastguard Worker                 if let Some(rsp) = full_rsp.borrow().as_ref() {
242*4185b066SAndroid Build Coastguard Worker                     assert_eq!(hex::encode(rsp), req);
243*4185b066SAndroid Build Coastguard Worker                 } else {
244*4185b066SAndroid Build Coastguard Worker                     panic!("no response received");
245*4185b066SAndroid Build Coastguard Worker                 }
246*4185b066SAndroid Build Coastguard Worker             }
247*4185b066SAndroid Build Coastguard Worker         }
248*4185b066SAndroid Build Coastguard Worker     }
249*4185b066SAndroid Build Coastguard Worker }
250