1use crate::error::HandshakeError;
8use crate::frame::{FrameType, ProtocolId};
9
10const STATUS_SUCCESS: u8 = 0;
11const STATUS_NO_COMMON_PROTOCOL: u8 = 1;
12const STATUS_MALFORMED_REQUEST: u8 = 2;
13
14const PROTOCOL_COUNT_LEN: usize = 1;
15const PROTOCOL_ID_LEN: usize = std::mem::size_of::<u32>();
16const STATUS_LEN: usize = 1;
17const RESPONSE_LEN: usize = STATUS_LEN + PROTOCOL_ID_LEN;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum HandshakeStatus {
22 Success,
24 NoCommonProtocol,
26 MalformedRequest,
28 Unknown(u8),
30}
31
32impl From<u8> for HandshakeStatus {
33 fn from(val: u8) -> Self {
34 match val {
35 STATUS_SUCCESS => HandshakeStatus::Success,
36 STATUS_NO_COMMON_PROTOCOL => HandshakeStatus::NoCommonProtocol,
37 STATUS_MALFORMED_REQUEST => HandshakeStatus::MalformedRequest,
38 other => HandshakeStatus::Unknown(other),
39 }
40 }
41}
42
43impl From<HandshakeStatus> for u8 {
44 fn from(status: HandshakeStatus) -> Self {
45 match status {
46 HandshakeStatus::Success => STATUS_SUCCESS,
47 HandshakeStatus::NoCommonProtocol => STATUS_NO_COMMON_PROTOCOL,
48 HandshakeStatus::MalformedRequest => STATUS_MALFORMED_REQUEST,
49 HandshakeStatus::Unknown(val) => val,
50 }
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
56pub(crate) struct HandshakeRequest {
57 pub protocols: Vec<ProtocolId>,
59}
60
61impl HandshakeRequest {
62 pub fn serialize(&self) -> Result<Vec<u8>, HandshakeError> {
64 if self.protocols.len() > u8::MAX as usize {
65 return Err(HandshakeError::MalformedPayload);
66 }
67 let mut data =
68 Vec::with_capacity(PROTOCOL_COUNT_LEN + PROTOCOL_ID_LEN * self.protocols.len());
69 data.push(self.protocols.len() as u8);
70 for &proto in &self.protocols {
71 data.extend_from_slice(&proto.wire_id().to_be_bytes());
72 }
73 Ok(data)
74 }
75}
76
77impl TryFrom<&[u8]> for HandshakeRequest {
78 type Error = HandshakeError;
79
80 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
82 if bytes.len() < PROTOCOL_COUNT_LEN {
83 return Err(HandshakeError::MalformedPayload);
84 }
85 let count = bytes[0] as usize;
86 let expected_len = PROTOCOL_COUNT_LEN + PROTOCOL_ID_LEN * count;
87 if bytes.len() < expected_len {
88 return Err(HandshakeError::MalformedPayload);
89 }
90 let protocols = bytes[PROTOCOL_COUNT_LEN..expected_len]
91 .chunks_exact(PROTOCOL_ID_LEN)
92 .map(|chunk| ProtocolId::from(u32::from_be_bytes(chunk.try_into().unwrap())))
93 .collect();
94 Ok(Self { protocols })
95 }
96}
97
98#[derive(Debug, Clone, PartialEq, Eq)]
100pub struct HandshakeResponse {
101 pub status: HandshakeStatus,
103 pub selected: Option<ProtocolId>,
105}
106
107impl HandshakeResponse {
108 pub fn serialize(&self) -> Result<Vec<u8>, HandshakeError> {
114 let mut data = Vec::with_capacity(RESPONSE_LEN);
115 data.push(u8::from(self.status));
116 let wire_id = self.selected.map(|p| p.wire_id()).unwrap_or(0);
117 data.extend_from_slice(&wire_id.to_be_bytes());
118 Ok(data)
119 }
120}
121
122impl TryFrom<&[u8]> for HandshakeResponse {
123 type Error = HandshakeError;
124
125 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
127 if bytes.len() < RESPONSE_LEN {
128 return Err(HandshakeError::MalformedPayload);
129 }
130 let status = HandshakeStatus::from(bytes[0]);
131 let val = u32::from_be_bytes(bytes[STATUS_LEN..RESPONSE_LEN].try_into().unwrap());
132 let selected =
133 if status == HandshakeStatus::Success { Some(ProtocolId::from(val)) } else { None };
134 Ok(Self { status, selected })
135 }
136}
137
138#[derive(Debug, Clone)]
140pub struct HostHandshake {
141 proposed: Vec<ProtocolId>,
142}
143
144impl HostHandshake {
145 pub fn new(proposed: Vec<ProtocolId>) -> Self {
147 Self { proposed }
148 }
149
150 pub fn proposed(&self) -> &[ProtocolId] {
152 &self.proposed
153 }
154
155 pub fn start(&self) -> Result<(FrameType, Vec<u8>), HandshakeError> {
161 let req = HandshakeRequest { protocols: self.proposed.clone() };
162 Ok((FrameType::NegotiateReq, req.serialize()?))
163 }
164
165 pub fn handle_response(&self, payload: &[u8]) -> Result<ProtocolId, HandshakeError> {
173 let resp = HandshakeResponse::try_from(payload)?;
174 match resp.status {
175 HandshakeStatus::Success => {
176 let selected = resp.selected.ok_or(HandshakeError::NoCommonProtocol)?;
177 if self.proposed.contains(&selected) {
178 Ok(selected)
179 } else {
180 Err(HandshakeError::ProtocolNotProposed)
181 }
182 }
183 HandshakeStatus::NoCommonProtocol => Err(HandshakeError::NoCommonProtocol),
184 HandshakeStatus::MalformedRequest => Err(HandshakeError::MalformedPayload),
185 HandshakeStatus::Unknown(code) => Err(HandshakeError::UnsupportedStatus(code)),
186 }
187 }
188}
189
190#[derive(Debug, Clone)]
203pub struct TargetHandshake {
204 supported: Vec<ProtocolId>,
205}
206
207impl TargetHandshake {
208 pub fn new(supported: Vec<ProtocolId>) -> Self {
210 Self { supported }
211 }
212
213 pub fn supported(&self) -> &[ProtocolId] {
215 &self.supported
216 }
217
218 pub fn handle_request(
225 &self,
226 payload: &[u8],
227 ) -> Result<(Option<ProtocolId>, Vec<u8>), HandshakeError> {
228 let req = HandshakeRequest::try_from(payload)?;
229 let selected = req.protocols.into_iter().find(|p| self.supported.contains(p));
230 let status = if selected.is_some() {
231 HandshakeStatus::Success
232 } else {
233 HandshakeStatus::NoCommonProtocol
234 };
235 let resp = HandshakeResponse { status, selected };
236 Ok((selected, resp.serialize()?))
237 }
238}
239
240#[cfg(test)]
241mod tests {
242 use super::*;
243
244 fn run_handshake(
245 host_proposed: Vec<ProtocolId>,
246 target_supported: Vec<ProtocolId>,
247 ) -> Result<ProtocolId, HandshakeError> {
248 let host = HostHandshake::new(host_proposed);
249 let target = TargetHandshake::new(target_supported);
250
251 let (_, req_payload) = host.start()?;
252 let (selected, resp_payload) = target.handle_request(&req_payload)?;
253 if selected.is_some() {
254 assert_eq!(resp_payload[0], u8::from(HandshakeStatus::Success));
255 }
256
257 host.handle_response(&resp_payload)
258 }
259
260 #[fuchsia::test]
261 fn test_handshake_matrix() {
262 struct TestCase {
263 host: Vec<ProtocolId>,
264 target: Vec<ProtocolId>,
265 expected: Result<ProtocolId, HandshakeError>,
266 }
267
268 let cases = [
269 TestCase {
270 host: vec![ProtocolId::ResendSP],
271 target: vec![ProtocolId::ResendSP],
272 expected: Ok(ProtocolId::ResendSP),
273 },
274 TestCase {
275 host: vec![ProtocolId::Unknown(99)],
276 target: vec![ProtocolId::Unknown(99)],
277 expected: Ok(ProtocolId::Unknown(99)),
278 },
279 TestCase {
281 host: vec![ProtocolId::Unknown(99), ProtocolId::ResendSP],
282 target: vec![ProtocolId::ResendSP, ProtocolId::Unknown(99)],
283 expected: Ok(ProtocolId::Unknown(99)),
284 },
285 TestCase {
287 host: vec![ProtocolId::Unknown(999), ProtocolId::ResendSP],
288 target: vec![ProtocolId::ResendSP],
289 expected: Ok(ProtocolId::ResendSP),
290 },
291 TestCase {
293 host: vec![ProtocolId::Unknown(42)],
294 target: vec![ProtocolId::Unknown(42)],
295 expected: Ok(ProtocolId::Unknown(42)),
296 },
297 TestCase {
299 host: vec![ProtocolId::ResendSP],
300 target: vec![ProtocolId::Unknown(99)],
301 expected: Err(HandshakeError::NoCommonProtocol),
302 },
303 TestCase {
305 host: vec![ProtocolId::ResendSP],
306 target: vec![],
307 expected: Err(HandshakeError::NoCommonProtocol),
308 },
309 ];
310
311 for (i, case) in cases.into_iter().enumerate() {
312 let result = run_handshake(case.host, case.target);
313 assert_eq!(result, case.expected, "Failed test case {i}");
314 }
315 }
316
317 #[fuchsia::test]
318 fn test_unproposed_protocol_selection_rejected_by_host() {
319 let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
320 let mut fake_resp = Vec::new();
321 fake_resp.push(u8::from(HandshakeStatus::Success));
322 fake_resp.extend_from_slice(&ProtocolId::Unknown(42).wire_id().to_be_bytes());
323
324 assert_eq!(host.handle_response(&fake_resp), Err(HandshakeError::ProtocolNotProposed));
325 }
326
327 #[fuchsia::test]
328 fn test_malformed_payload_rejections() {
329 let target = TargetHandshake::new(vec![ProtocolId::ResendSP]);
330 assert_eq!(target.handle_request(&[][..]), Err(HandshakeError::MalformedPayload));
331 assert_eq!(
333 target.handle_request(&[2, 0, 0, 0, 1][..]),
334 Err(HandshakeError::MalformedPayload)
335 );
336
337 let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
338 assert_eq!(host.handle_response(&[][..]), Err(HandshakeError::MalformedPayload));
339 assert_eq!(host.handle_response(&[0; 4][..]), Err(HandshakeError::MalformedPayload));
340
341 let overflow_host = HostHandshake::new(vec![ProtocolId::ResendSP; 256]);
343 assert_eq!(overflow_host.start(), Err(HandshakeError::MalformedPayload));
344 }
345
346 #[fuchsia::test]
347 fn test_unsupported_status_rejection() {
348 let host = HostHandshake::new(vec![ProtocolId::ResendSP]);
349 let fake_resp = vec![99u8, 0, 0, 0, 1];
350 assert_eq!(host.handle_response(&fake_resp), Err(HandshakeError::UnsupportedStatus(99)));
351
352 assert_eq!(HandshakeStatus::from(99), HandshakeStatus::Unknown(99));
353 assert_eq!(u8::from(HandshakeStatus::Unknown(99)), 99);
354 }
355}