Skip to main content

osdp/secure/
pad.rs

1//! 0x80-padding rules used by the secure channel.
2//!
3//! # Spec: Annex D.5
4//!
5//! - **MAC**: pad input with `0x80` then zeros only if it isn't already a
6//!   multiple of 16.
7//! - **DATA encryption (SCS_17/18)**: pad with `0x80` then zeros, *always*
8//!   (even when the input is already a multiple of 16).
9
10/// Append 0x80-padding for MAC inputs ("only if needed").
11pub fn pad_mac(buf: &mut alloc::vec::Vec<u8>) {
12    let rem = buf.len() % 16;
13    if rem == 0 {
14        return;
15    }
16    buf.push(0x80);
17    let need = 16 - ((rem + 1) % 16);
18    if need != 16 {
19        buf.extend(core::iter::repeat_n(0u8, need));
20    }
21}
22
23/// Append 0x80-padding for encrypted DATA ("always").
24pub fn pad_data(buf: &mut alloc::vec::Vec<u8>) {
25    buf.push(0x80);
26    let rem = buf.len() % 16;
27    if rem != 0 {
28        buf.extend(core::iter::repeat_n(0u8, 16 - rem));
29    }
30}
31
32/// Strip 0x80-padding from decrypted DATA. Returns the stripped slice.
33///
34/// Errors if the trailing bytes do not match `0x80 [0x00]*`.
35pub fn unpad_data(buf: &[u8]) -> Result<&[u8], crate::error::SecureSessionError> {
36    let mut i = buf.len();
37    while i > 0 {
38        i -= 1;
39        match buf[i] {
40            0x00 => continue,
41            0x80 => return Ok(&buf[..i]),
42            _ => return Err(crate::error::SecureSessionError::BadPadding),
43        }
44    }
45    Err(crate::error::SecureSessionError::BadPadding)
46}
47
48#[cfg(test)]
49mod tests {
50    use super::*;
51    use alloc::vec::Vec;
52
53    #[test]
54    fn mac_padding_skipped_when_aligned() {
55        let mut buf: Vec<u8> = (0..16).collect();
56        pad_mac(&mut buf);
57        assert_eq!(buf.len(), 16);
58    }
59
60    #[test]
61    fn mac_padding_when_unaligned() {
62        let mut buf: Vec<u8> = (0..3).collect();
63        pad_mac(&mut buf);
64        assert_eq!(buf.len(), 16);
65        assert_eq!(buf[3], 0x80);
66        assert!(buf[4..].iter().all(|&b| b == 0));
67    }
68
69    #[test]
70    fn data_padding_always_applied() {
71        let mut buf: Vec<u8> = (0..16).collect();
72        pad_data(&mut buf);
73        assert_eq!(buf.len(), 32);
74        assert_eq!(buf[16], 0x80);
75    }
76
77    #[test]
78    fn unpad_roundtrip() {
79        for n in 0..32usize {
80            let mut buf: Vec<u8> = (0..n as u8).collect();
81            let original = buf.clone();
82            pad_data(&mut buf);
83            let stripped = unpad_data(&buf).unwrap();
84            assert_eq!(stripped, original);
85        }
86    }
87}