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