Skip to main content

stackforge_core/layer/dot15d4/
security.rs

1//! IEEE 802.15.4 Auxiliary Security Header parsing and building.
2//!
3//! The Auxiliary Security Header is present when the Security Enabled bit
4//! is set in the Frame Control Field. It consists of:
5//! - Security Control (1 byte)
6//! - Frame Counter (4 bytes, LE)
7//! - Key Identifier (variable length, based on Key Identifier Mode)
8
9use crate::layer::field::{FieldError, read_u32_le, read_u64_le};
10
11use super::types;
12
13/// Security Control field bit layout (1 byte):
14/// Bits 0-2: Security Level (3 bits)
15/// Bits 3-4: Key Identifier Mode (2 bits)
16/// Bits 5-7: Reserved (3 bits)
17
18/// Parsed representation of an Auxiliary Security Header.
19#[derive(Debug, Clone, PartialEq, Eq, Default)]
20pub struct AuxSecurityHeader {
21    /// Security level (3 bits).
22    pub security_level: u8,
23    /// Key identifier mode (2 bits).
24    pub key_id_mode: u8,
25    /// Reserved bits (3 bits).
26    pub reserved: u8,
27    /// Frame counter (4 bytes).
28    pub frame_counter: u32,
29    /// Key source (0, 4, or 8 bytes depending on `key_id_mode`).
30    pub key_source: u64,
31    /// Key index (1 byte, present if `key_id_mode` != 0).
32    pub key_index: Option<u8>,
33}
34
35impl AuxSecurityHeader {
36    /// Total byte length of this security header.
37    #[must_use]
38    pub fn len(&self) -> usize {
39        // 1 (security control) + 4 (frame counter) + key_id_len
40        1 + 4 + types::key_id_len(self.key_id_mode)
41    }
42
43    /// Compute the byte length of the security header given the security
44    /// control byte (first byte of the header).
45    #[must_use]
46    pub fn compute_len(security_control: u8) -> usize {
47        let key_id_mode = (security_control >> 3) & 0x03;
48        1 + 4 + types::key_id_len(key_id_mode)
49    }
50
51    /// Parse an Auxiliary Security Header from the buffer at the given offset.
52    /// Returns the parsed header and the number of bytes consumed.
53    pub fn parse(buf: &[u8], offset: usize) -> Result<(Self, usize), FieldError> {
54        // Need at least 5 bytes (1 security control + 4 frame counter)
55        if buf.len() < offset + 5 {
56            return Err(FieldError::BufferTooShort {
57                offset,
58                need: 5,
59                have: buf.len().saturating_sub(offset),
60            });
61        }
62
63        let sc = buf[offset];
64        let security_level = sc & 0x07;
65        let key_id_mode = (sc >> 3) & 0x03;
66        let reserved = (sc >> 5) & 0x07;
67
68        let frame_counter = read_u32_le(buf, offset + 1)?;
69
70        let ki_len = types::key_id_len(key_id_mode);
71        let total_len = 1 + 4 + ki_len;
72
73        if buf.len() < offset + total_len {
74            return Err(FieldError::BufferTooShort {
75                offset,
76                need: total_len,
77                have: buf.len().saturating_sub(offset),
78            });
79        }
80
81        let mut key_source: u64 = 0;
82        let key_index: Option<u8>;
83
84        match key_id_mode {
85            0 => {
86                // Implicit: no key identifier
87                key_index = None;
88            },
89            1 => {
90                // Key Index only (1 byte)
91                key_index = Some(buf[offset + 5]);
92            },
93            2 => {
94                // 4-byte Key Source + 1-byte Key Index
95                key_source = u64::from(read_u32_le(buf, offset + 5)?);
96                key_index = Some(buf[offset + 9]);
97            },
98            3 => {
99                // 8-byte Key Source + 1-byte Key Index
100                key_source = read_u64_le(buf, offset + 5)?;
101                key_index = Some(buf[offset + 13]);
102            },
103            _ => {
104                key_index = None;
105            },
106        }
107
108        Ok((
109            Self {
110                security_level,
111                key_id_mode,
112                reserved,
113                frame_counter,
114                key_source,
115                key_index,
116            },
117            total_len,
118        ))
119    }
120
121    /// Build the security header bytes.
122    #[must_use]
123    pub fn build(&self) -> Vec<u8> {
124        let mut out = Vec::with_capacity(self.len());
125
126        // Security Control byte
127        let sc = (self.security_level & 0x07)
128            | ((self.key_id_mode & 0x03) << 3)
129            | ((self.reserved & 0x07) << 5);
130        out.push(sc);
131
132        // Frame Counter (4 bytes, LE)
133        out.extend_from_slice(&self.frame_counter.to_le_bytes());
134
135        // Key Identifier
136        match self.key_id_mode {
137            0 => {
138                // No key identifier
139            },
140            1 => {
141                // Key Index only
142                out.push(self.key_index.unwrap_or(0xFF));
143            },
144            2 => {
145                // 4-byte Key Source (LE) + Key Index
146                out.extend_from_slice(&(self.key_source as u32).to_le_bytes());
147                out.push(self.key_index.unwrap_or(0xFF));
148            },
149            3 => {
150                // 8-byte Key Source (LE) + Key Index
151                out.extend_from_slice(&self.key_source.to_le_bytes());
152                out.push(self.key_index.unwrap_or(0xFF));
153            },
154            _ => {},
155        }
156
157        out
158    }
159
160    /// Write the security header into a buffer at the given offset.
161    /// Returns the number of bytes written.
162    pub fn write_into(&self, buf: &mut [u8], offset: usize) -> Result<usize, FieldError> {
163        let bytes = self.build();
164        if buf.len() < offset + bytes.len() {
165            return Err(FieldError::BufferTooShort {
166                offset,
167                need: bytes.len(),
168                have: buf.len().saturating_sub(offset),
169            });
170        }
171        buf[offset..offset + bytes.len()].copy_from_slice(&bytes);
172        Ok(bytes.len())
173    }
174}
175
176/// Read the security level from a Security Control byte.
177#[inline]
178#[must_use]
179pub fn read_security_level(sc: u8) -> u8 {
180    sc & 0x07
181}
182
183/// Read the key identifier mode from a Security Control byte.
184#[inline]
185#[must_use]
186pub fn read_key_id_mode(sc: u8) -> u8 {
187    (sc >> 3) & 0x03
188}
189
190#[cfg(test)]
191mod tests {
192    use super::*;
193
194    #[test]
195    fn test_parse_implicit_key() {
196        // Security Control: level=0 (None), key_id_mode=0 (Implicit), reserved=0
197        // Frame Counter: 0x00000000
198        let buf = [0x00, 0x00, 0x00, 0x00, 0x00];
199        let (hdr, consumed) = AuxSecurityHeader::parse(&buf, 0).unwrap();
200        assert_eq!(hdr.security_level, 0);
201        assert_eq!(hdr.key_id_mode, 0);
202        assert_eq!(hdr.reserved, 0);
203        assert_eq!(hdr.frame_counter, 0);
204        assert_eq!(hdr.key_index, None);
205        assert_eq!(consumed, 5);
206    }
207
208    #[test]
209    fn test_parse_key_index_mode() {
210        // Security Control: level=1 (MIC-32), key_id_mode=1, reserved=0
211        // SC = 0b00_01_001 = 0x09
212        // Frame Counter: 0x12345678 (LE)
213        // Key Index: 0x42
214        let buf = [0x09, 0x78, 0x56, 0x34, 0x12, 0x42];
215        let (hdr, consumed) = AuxSecurityHeader::parse(&buf, 0).unwrap();
216        assert_eq!(hdr.security_level, 1);
217        assert_eq!(hdr.key_id_mode, 1);
218        assert_eq!(hdr.frame_counter, 0x12345678);
219        assert_eq!(hdr.key_index, Some(0x42));
220        assert_eq!(consumed, 6);
221    }
222
223    #[test]
224    fn test_parse_key_source_4() {
225        // Security Control: level=5 (ENC-MIC-32), key_id_mode=2, reserved=0
226        // SC = 0b00_10_101 = 0x15
227        // Frame Counter: 0x00000001 (LE)
228        // Key Source (4 bytes LE): 0xAABBCCDD
229        // Key Index: 0x01
230        let buf = [0x15, 0x01, 0x00, 0x00, 0x00, 0xDD, 0xCC, 0xBB, 0xAA, 0x01];
231        let (hdr, consumed) = AuxSecurityHeader::parse(&buf, 0).unwrap();
232        assert_eq!(hdr.security_level, 5);
233        assert_eq!(hdr.key_id_mode, 2);
234        assert_eq!(hdr.frame_counter, 1);
235        assert_eq!(hdr.key_source, 0xAABBCCDD);
236        assert_eq!(hdr.key_index, Some(0x01));
237        assert_eq!(consumed, 10);
238    }
239
240    #[test]
241    fn test_parse_key_source_8() {
242        // Security Control: level=7 (ENC-MIC-128), key_id_mode=3, reserved=0
243        // SC = 0b00_11_111 = 0x1F
244        // Frame Counter: 0x00000002 (LE)
245        // Key Source (8 bytes LE): 0x0102030405060708
246        // Key Index: 0xFF
247        let buf = [
248            0x1F, 0x02, 0x00, 0x00, 0x00, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0xFF,
249        ];
250        let (hdr, consumed) = AuxSecurityHeader::parse(&buf, 0).unwrap();
251        assert_eq!(hdr.security_level, 7);
252        assert_eq!(hdr.key_id_mode, 3);
253        assert_eq!(hdr.frame_counter, 2);
254        assert_eq!(hdr.key_source, 0x0102030405060708);
255        assert_eq!(hdr.key_index, Some(0xFF));
256        assert_eq!(consumed, 14);
257    }
258
259    #[test]
260    fn test_build_roundtrip_implicit() {
261        let hdr = AuxSecurityHeader {
262            security_level: 0,
263            key_id_mode: 0,
264            reserved: 0,
265            frame_counter: 0,
266            key_source: 0,
267            key_index: None,
268        };
269        let bytes = hdr.build();
270        assert_eq!(bytes.len(), 5);
271        let (parsed, consumed) = AuxSecurityHeader::parse(&bytes, 0).unwrap();
272        assert_eq!(consumed, 5);
273        assert_eq!(parsed, hdr);
274    }
275
276    #[test]
277    fn test_build_roundtrip_key_index() {
278        let hdr = AuxSecurityHeader {
279            security_level: 1,
280            key_id_mode: 1,
281            reserved: 0,
282            frame_counter: 0x12345678,
283            key_source: 0,
284            key_index: Some(0x42),
285        };
286        let bytes = hdr.build();
287        assert_eq!(bytes.len(), 6);
288        let (parsed, _) = AuxSecurityHeader::parse(&bytes, 0).unwrap();
289        assert_eq!(parsed, hdr);
290    }
291
292    #[test]
293    fn test_build_roundtrip_key_source_4() {
294        let hdr = AuxSecurityHeader {
295            security_level: 5,
296            key_id_mode: 2,
297            reserved: 0,
298            frame_counter: 1,
299            key_source: 0xAABBCCDD,
300            key_index: Some(0x01),
301        };
302        let bytes = hdr.build();
303        assert_eq!(bytes.len(), 10);
304        let (parsed, _) = AuxSecurityHeader::parse(&bytes, 0).unwrap();
305        assert_eq!(parsed, hdr);
306    }
307
308    #[test]
309    fn test_build_roundtrip_key_source_8() {
310        let hdr = AuxSecurityHeader {
311            security_level: 7,
312            key_id_mode: 3,
313            reserved: 0,
314            frame_counter: 2,
315            key_source: 0x0102030405060708,
316            key_index: Some(0xFF),
317        };
318        let bytes = hdr.build();
319        assert_eq!(bytes.len(), 14);
320        let (parsed, _) = AuxSecurityHeader::parse(&bytes, 0).unwrap();
321        assert_eq!(parsed, hdr);
322    }
323
324    #[test]
325    fn test_parse_with_offset() {
326        // Prepend some garbage bytes
327        let mut buf = vec![0xAA, 0xBB, 0xCC];
328        let hdr = AuxSecurityHeader {
329            security_level: 4,
330            key_id_mode: 1,
331            reserved: 0,
332            frame_counter: 100,
333            key_source: 0,
334            key_index: Some(0x05),
335        };
336        buf.extend_from_slice(&hdr.build());
337        let (parsed, consumed) = AuxSecurityHeader::parse(&buf, 3).unwrap();
338        assert_eq!(parsed, hdr);
339        assert_eq!(consumed, 6);
340    }
341
342    #[test]
343    fn test_parse_buffer_too_short() {
344        let buf = [0x00, 0x00]; // Too short for even minimal header
345        let result = AuxSecurityHeader::parse(&buf, 0);
346        assert!(result.is_err());
347    }
348
349    #[test]
350    fn test_compute_len() {
351        assert_eq!(AuxSecurityHeader::compute_len(0x00), 5); // mode 0
352        assert_eq!(AuxSecurityHeader::compute_len(0x08), 6); // mode 1
353        assert_eq!(AuxSecurityHeader::compute_len(0x10), 10); // mode 2
354        assert_eq!(AuxSecurityHeader::compute_len(0x18), 14); // mode 3
355    }
356
357    #[test]
358    fn test_write_into() {
359        let hdr = AuxSecurityHeader {
360            security_level: 1,
361            key_id_mode: 1,
362            reserved: 0,
363            frame_counter: 42,
364            key_source: 0,
365            key_index: Some(0x10),
366        };
367        let mut buf = vec![0u8; 20];
368        let written = hdr.write_into(&mut buf, 2).unwrap();
369        assert_eq!(written, 6);
370        let (parsed, _) = AuxSecurityHeader::parse(&buf, 2).unwrap();
371        assert_eq!(parsed, hdr);
372    }
373
374    #[test]
375    fn test_read_security_level() {
376        assert_eq!(read_security_level(0x00), 0);
377        assert_eq!(read_security_level(0x07), 7);
378        assert_eq!(read_security_level(0xFF), 7);
379    }
380
381    #[test]
382    fn test_read_key_id_mode() {
383        assert_eq!(read_key_id_mode(0x00), 0);
384        assert_eq!(read_key_id_mode(0x08), 1);
385        assert_eq!(read_key_id_mode(0x10), 2);
386        assert_eq!(read_key_id_mode(0x18), 3);
387    }
388}