ytls_extensions/
compress_certificate.rs

1//! yTLS Extension (27) Compress Certificate support
2
3use crate::TlsExtError;
4use ytls_typed::CertificateCompressKind;
5
6/// Downstream Compress Certificate Processor
7pub trait ExtCompressCertProcessor {
8    /// Client indicates support for a given Certificate Compression algorithm.
9    fn compress_certificate(&mut self, _: CertificateCompressKind) -> ();
10}
11
12/// TLS Extension 27 Compress Certificate Support
13pub struct TlsExtCompressCert {}
14
15impl TlsExtCompressCert {
16    /// Parse all the supported certificate compression algorithms.
17    #[inline]
18    pub fn client_compress_certificate_cb<P: ExtCompressCertProcessor>(
19        p: &mut P,
20        compress_alg_raw: &[u8],
21    ) -> Result<(), TlsExtError> {
22        if compress_alg_raw.len() < 1 {
23            return Err(TlsExtError::InvalidLength);
24        }
25        let compress_algs_len = compress_alg_raw[0];
26
27        if compress_algs_len == 0 {
28            return Err(TlsExtError::NoData);
29        }
30
31        if compress_alg_raw.len() < 4
32            || compress_algs_len as usize != compress_alg_raw.len() - 1
33            || compress_algs_len % 2 != 0
34        {
35            return Err(TlsExtError::InvalidLength);
36        }
37
38        let remaining = &compress_alg_raw[1..];
39        let expected_len = remaining.len();
40
41        if compress_algs_len as usize != expected_len {
42            return Err(TlsExtError::InvalidLength);
43        }
44
45        let mut comp_algs_i = remaining.chunks(2);
46
47        while let Some(comp_alg) = comp_algs_i.next() {
48            let comp_alg_id = u16::from_be_bytes([comp_alg[0], comp_alg[1]]);
49            p.compress_certificate(comp_alg_id.into());
50        }
51        Ok(())
52    }
53}
54
55#[cfg(test)]
56mod test {
57    use super::*;
58    use hex_literal::hex;
59    use rstest::rstest;
60    use ytls_typed::CertificateCompressKind as Cck;
61
62    #[derive(Debug, Default, PartialEq)]
63    struct Tester {
64        seen: Vec<Cck>,
65    }
66
67    impl ExtCompressCertProcessor for Tester {
68        fn compress_certificate(&mut self, comp_alg: Cck) -> () {
69            self.seen.push(comp_alg);
70        }
71    }
72
73    #[rstest]
74    #[case(
75        "06000100020003",
76        Tester { seen: vec![Cck::Zlib, Cck::Brotli, Cck::Zstd] },
77        Ok(())
78    )]
79    #[case(
80        "",
81        Tester { seen: vec![] },
82        Err(TlsExtError::InvalidLength)
83    )]
84    fn client_compress_certificate(
85        #[case] raw_t: &str,
86        #[case] expected_tester: Tester,
87        #[case] expected_res: Result<(), TlsExtError>,
88    ) {
89        let in_raw = hex::decode(raw_t).unwrap();
90        let mut tester = Tester::default();
91        let res = TlsExtCompressCert::client_compress_certificate_cb(&mut tester, &in_raw);
92        assert_eq!(expected_tester, tester);
93        assert_eq!(expected_res, res);
94    }
95}