Skip to main content

hatchery_core/
inner.rs

1//! Inner-layer tagging inside restreamer DATA payloads (A2 / A3).
2//!
3//! After C2 strips its outer layer the remaining bytes are `tag || body`.
4//! C2 never reads the tag; only the two ends of the inner Noise session do.
5
6/// Noise_NK message 1 (`-> e, es`), node to core.
7pub const TAG_HANDSHAKE_1: u8 = 1;
8/// Noise_NK message 2 (`<- e, ee`), core to node.
9pub const TAG_HANDSHAKE_2: u8 = 2;
10/// Noise transport ciphertext, either direction.
11pub const TAG_TRANSPORT: u8 = 3;
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
14pub enum InnerKind {
15    Handshake1,
16    Handshake2,
17    Transport,
18}
19
20impl InnerKind {
21    pub fn tag(self) -> u8 {
22        match self {
23            Self::Handshake1 => TAG_HANDSHAKE_1,
24            Self::Handshake2 => TAG_HANDSHAKE_2,
25            Self::Transport => TAG_TRANSPORT,
26        }
27    }
28}
29
30pub fn encode_inner(kind: InnerKind, body: &[u8]) -> Vec<u8> {
31    let mut out = Vec::with_capacity(1 + body.len());
32    out.push(kind.tag());
33    out.extend_from_slice(body);
34    out
35}
36
37pub fn decode_inner(bytes: &[u8]) -> Option<(InnerKind, &[u8])> {
38    let (&tag, body) = bytes.split_first()?;
39    let kind = match tag {
40        TAG_HANDSHAKE_1 => InnerKind::Handshake1,
41        TAG_HANDSHAKE_2 => InnerKind::Handshake2,
42        TAG_TRANSPORT => InnerKind::Transport,
43        _ => return None,
44    };
45    Some((kind, body))
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51
52    #[test]
53    fn roundtrip_and_unknown_tag() {
54        let b = encode_inner(InnerKind::Transport, b"x");
55        assert_eq!(decode_inner(&b), Some((InnerKind::Transport, &b"x"[..])));
56        assert_eq!(decode_inner(&[9, 1]), None);
57        assert_eq!(decode_inner(&[]), None);
58    }
59}