ytls_record/record/handshake/
cipher_suites.rs

1//! CipherSuites parsing
2//! This does not validate IANA registry conformance
3//! which is the responsibility of the downstream
4//! impl HelloProcessor.
5
6use ytls_traits::ClientHelloProcessor;
7
8use crate::error::CipherSuitesError;
9
10use zerocopy::byteorder::network_endian::U16 as N16;
11
12/// CipherSuites parsing & building
13pub struct CipherSuites {}
14
15impl CipherSuites {
16    /// Parse cipher suites from the byte slice and pass them to the provided ClientHelloProcessor
17    pub fn parse_cipher_suites<P: ClientHelloProcessor>(
18        prc: &mut P,
19        bytes: &[u8],
20    ) -> Result<(), CipherSuitesError> {
21        if bytes.len() % 2 != 0 || bytes.len() == 0 {
22            return Err(CipherSuitesError::InvalidLength);
23        }
24
25        let mut cs_iter = bytes.chunks(2);
26
27        while let Some(cs) = cs_iter.next() {
28            prc.handle_cipher_suite(&[cs[0], cs[1]]);
29        }
30        Ok(())
31    }
32}
33
34#[cfg(test)]
35mod test {
36
37    use super::*;
38    use rstest::rstest;
39
40    #[derive(Debug, PartialEq)]
41    struct Tester {
42        suites_encountered: Vec<[u8; 2]>,
43    }
44    impl ClientHelloProcessor for Tester {
45        fn handle_extension(&mut self, _ext_id: u16, _ext_data: &[u8]) -> () {
46            unreachable!()
47        }
48        fn handle_cipher_suite(&mut self, cipher_suite: &[u8; 2]) -> () {
49            self.suites_encountered
50                .push([cipher_suite[0], cipher_suite[1]]);
51        }
52        fn handle_client_random(&mut self, _: &[u8; 32]) {
53            unreachable!()
54        }
55        fn handle_session_id(&mut self, _: &[u8]) {
56            unreachable!()
57        }
58    }
59
60    use hex_literal::hex;
61
62    // Firefox as client wants these cipher suites
63    #[rstest]
64    #[case("130113031302c02bc02fcca9cca8c02cc030c00ac009c013c014009c009d002f0035",
65           Ok(()),
66           Tester { suites_encountered: vec![
67               hex!("1301"), hex!("1303"), hex!("1302"), hex!("c02b"),
68               hex!("c02f"), hex!("cca9"), hex!("cca8"), hex!("c02c"),
69               hex!("c030"), hex!("c00a"), hex!("c009"), hex!("c013"),
70               hex!("c014"), hex!("009c"), hex!("009d"), hex!("002f"),
71               hex!("0035")]
72           }    )]
73    /// Cipher suites must be % 4 == 0
74    #[case("130113031302c02bc02fcca9cca8c02cc030c00ac009c013c014009c009d002f00",
75           Err(CipherSuitesError::InvalidLength),
76           Tester { suites_encountered: vec![] }
77    )]
78    fn t_cipher_suites_parsing(
79        #[case] input: &'static str,
80        #[case] res: Result<(), CipherSuitesError>,
81        #[case] exp: Tester,
82    ) {
83        let mut prc = Tester {
84            suites_encountered: vec![],
85        };
86
87        let r = CipherSuites::parse_cipher_suites(&mut prc, &hex::decode(input).unwrap());
88        assert_eq!(r, res);
89        assert_eq!(exp, prc);
90    }
91}