macula_rust/frame/
session.rs1use crate::cbor::Value;
9
10use super::neighbour::control_header;
11use super::{base, entry, FrameError};
12
13pub const LIVENESS_NONCE_SIZE: usize = 16;
15
16pub fn verify_session_frame(frame: &Value) -> Result<Value, FrameError> {
19 let Value::Map(pairs) = frame else {
20 return Err(FrameError::Malformed);
21 };
22 match control_header(pairs) {
23 (_, _, true) => Err(FrameError::Malformed),
24 _ => Ok(frame.clone()),
25 }
26}
27
28pub fn liveness_ping_frame(nonce: &[u8; LIVENESS_NONCE_SIZE]) -> Value {
30 liveness_frame("liveness_ping", nonce)
31}
32
33pub fn liveness_pong_frame(nonce: &[u8; LIVENESS_NONCE_SIZE]) -> Value {
36 liveness_frame("liveness_pong", nonce)
37}
38
39fn liveness_frame(frame_type: &str, nonce: &[u8; LIVENESS_NONCE_SIZE]) -> Value {
40 let mut fields = base(frame_type);
41 fields.push(entry("nonce", Value::Bytes(nonce.to_vec())));
42 Value::Map(fields)
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub enum Liveness {
48 Ping,
49 Pong,
50}
51
52pub fn liveness_nonce(frame: &Value) -> Option<(Liveness, [u8; LIVENESS_NONCE_SIZE])> {
55 let Value::Map(pairs) = frame else {
56 return None;
57 };
58 let kind = match control_header(pairs).0.as_str() {
59 "liveness_ping" => Liveness::Ping,
60 "liveness_pong" => Liveness::Pong,
61 _ => return None,
62 };
63 match frame.get("nonce") {
64 Some(Value::Bytes(nonce)) => Some((kind, nonce.as_slice().try_into().ok()?)),
65 _ => None,
66 }
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72 use crate::frame::{goodbye_frame, neighbour_signed};
73 use crate::profile::Profile;
74
75 #[test]
76 fn a_v5_connection_reads_frames_without_a_neighbour_signature_and_refuses_one_that_has_it() {
77 let ping = liveness_ping_frame(&[7; 16]);
78 assert_eq!(verify_session_frame(&ping).unwrap(), ping);
79 let Value::Map(mut goodbye) = goodbye_frame("bye", None).unwrap() else {
80 panic!("a goodbye is a map");
81 };
82 goodbye.push(entry("neighbour", Value::Map(Vec::new())));
83 assert_eq!(
84 verify_session_frame(&Value::Map(goodbye)),
85 Err(FrameError::Malformed)
86 );
87 }
88
89 #[test]
90 fn liveness_frames_carry_their_nonce_and_are_never_neighbour_signed() {
91 let nonce = [1; 16];
92 for (kind, frame, name) in [
93 (Liveness::Ping, liveness_ping_frame(&nonce), "liveness_ping"),
94 (Liveness::Pong, liveness_pong_frame(&nonce), "liveness_pong"),
95 ] {
96 assert_eq!(liveness_nonce(&frame), Some((kind, nonce)), "{name}");
97 assert!(!neighbour_signed(Profile::PqHybrid, name), "{name}");
98 let wire = crate::frame::encode(&frame).unwrap();
99 let crate::frame::Decoded::Complete { frame: decoded, .. } =
100 crate::frame::decode(&wire).unwrap()
101 else {
102 panic!("{name}: not whole");
103 };
104 assert_eq!(liveness_nonce(&decoded), Some((kind, nonce)), "{name}");
105 }
106 let mut short = base("liveness_ping");
107 short.push(entry("nonce", Value::Bytes(vec![1, 2])));
108 assert_eq!(liveness_nonce(&Value::Map(short)), None);
109 assert_eq!(liveness_nonce(&goodbye_frame("bye", None).unwrap()), None);
110 }
111}