webrtc_dtls/crypto/
padding.rs

1use cbc::cipher::block_padding::{PadType, RawPadding, UnpadError};
2use core::panic;
3
4pub enum DtlsPadding {}
5/// Reference: RFC5246, 6.2.3.2
6impl RawPadding for DtlsPadding {
7    const TYPE: PadType = PadType::Reversible;
8
9    fn raw_pad(block: &mut [u8], pos: usize) {
10        if pos >= block.len() {
11            panic!("`pos` is bigger or equal to block size");
12        }
13
14        let padding_length = block.len() - pos - 1;
15        if padding_length > 255 {
16            panic!("block size is too big for DTLS");
17        }
18
19        set(&mut block[pos..], padding_length as u8);
20    }
21
22    fn raw_unpad(data: &[u8]) -> Result<&[u8], UnpadError> {
23        let padding_length = data.last().copied().unwrap_or(1) as usize;
24        if padding_length + 1 > data.len() {
25            return Err(UnpadError);
26        }
27
28        let padding_begin = data.len() - padding_length - 1;
29
30        if data[padding_begin..data.len() - 1]
31            .iter()
32            .any(|&byte| byte as usize != padding_length)
33        {
34            return Err(UnpadError);
35        }
36
37        Ok(&data[0..padding_begin])
38    }
39}
40
41/// Sets all bytes in `dst` equal to `value`
42#[inline(always)]
43fn set(dst: &mut [u8], value: u8) {
44    // SAFETY: we overwrite valid memory behind `dst`
45    // note: loop is not used here because it produces
46    // unnecessary branch which tests for zero-length slices
47    unsafe {
48        core::ptr::write_bytes(dst.as_mut_ptr(), value, dst.len());
49    }
50}
51
52#[cfg(test)]
53pub mod tests {
54    use rand::Rng;
55
56    use super::*;
57
58    #[test]
59    fn padding_length_is_amount_of_bytes_excluding_the_padding_length_itself() -> Result<(), ()> {
60        for original_length in 0..128 {
61            for padding_length in 0..(256 - original_length) {
62                let mut block = vec![0; original_length + padding_length + 1];
63                rand::thread_rng().fill(&mut block[0..original_length]);
64                let original = block[0..original_length].to_vec();
65                DtlsPadding::raw_pad(&mut block, original_length);
66
67                for byte in block[original_length..].iter() {
68                    assert_eq!(*byte as usize, padding_length);
69                }
70                assert_eq!(block[0..original_length], original);
71            }
72        }
73
74        Ok(())
75    }
76
77    #[test]
78    #[should_panic]
79    fn full_block_is_padding_error() {
80        for original_length in 0..256 {
81            let mut block = vec![0; original_length];
82            DtlsPadding::raw_pad(&mut block, original_length);
83        }
84    }
85
86    #[test]
87    #[should_panic]
88    fn padding_length_bigger_than_255_is_a_pad_error() {
89        let padding_length = 256;
90        for original_length in 0..128 {
91            let mut block = vec![0; original_length + padding_length + 1];
92            DtlsPadding::raw_pad(&mut block, original_length);
93        }
94    }
95
96    #[test]
97    fn empty_block_is_unpadding_error() {
98        let r = DtlsPadding::raw_unpad(&[]);
99        assert!(r.is_err());
100    }
101
102    #[test]
103    fn padding_too_big_for_block_is_unpadding_error() {
104        let r = DtlsPadding::raw_unpad(&[1]);
105        assert!(r.is_err());
106    }
107
108    #[test]
109    fn one_of_the_padding_bytes_with_value_different_than_padding_length_is_unpadding_error() {
110        for padding_length in 0..16 {
111            for invalid_byte in 0..padding_length {
112                let mut block = vec![0; padding_length + 1];
113                DtlsPadding::raw_pad(&mut block, 0);
114
115                assert_eq!(DtlsPadding::raw_unpad(&block).ok(), Some(&[][..]));
116                block[invalid_byte] = (padding_length - 1) as u8;
117                let r = DtlsPadding::raw_unpad(&block);
118                assert!(r.is_err());
119            }
120        }
121    }
122}