Skip to main content

rust_jarm/
lib.rs

1pub mod error;
2
3use rand::RngExt;
4use std::str::FromStr;
5use sha2::{Sha256, Digest};
6use std::net::{SocketAddr, TcpStream, ToSocketAddrs};
7use std::io::{Write, Read};
8use std::time::Duration;
9use rand::seq::IndexedRandom;
10use crate::error::{DetailedError, JarmError};
11
12const ALPN_EXTENSION: &[u8; 2] = b"\x00\x10";
13const SOCKET_BUFFER: u64 = 1484;
14const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
15
16
17pub struct JarmPart {
18    pub raw: String
19}
20
21impl JarmPart {
22    pub fn new(raw: &str) -> JarmPart {
23        JarmPart {
24            raw: raw.to_string(),
25        }
26    }
27}
28
29#[non_exhaustive]
30pub struct Jarm {
31    pub parts: Vec<JarmPart>,
32    pub queue: Vec<PacketSpecification>,
33    pub rng: Box<dyn JarmRng + 'static>,
34    pub timeout: Duration,
35}
36
37impl Default for Jarm {
38    fn default() -> Self {
39        Jarm::new("localhost".to_string(), "80".to_string())
40    }
41}
42
43impl Jarm {
44    pub fn new(host: String, port: String) -> Jarm {
45        Jarm {
46            parts: Vec::new(),
47            queue: vec![
48                //<editor-fold defaultstate="collapsed" desc="Packets and formats to send">
49                PacketSpecification {
50                    host: host.clone(),
51                    port: port.clone(),
52                    tls_version: TlsVersion::TLS1_2,
53                    cipher_list: CipherList::ALL,
54                    cipher_order: CipherOrder::FORWARD,
55                    use_grease: false,
56                    use_rare_apln: false,
57                    tls_version_support: TlsVersionSupport::TLS1_2,
58                    extension_order: CipherOrder::REVERSE,
59                },
60                PacketSpecification {
61                    host: host.clone(),
62                    port: port.clone(),
63                    tls_version: TlsVersion::TLS1_2,
64                    cipher_list: CipherList::ALL,
65                    cipher_order: CipherOrder::REVERSE,
66                    use_grease: false,
67                    use_rare_apln: false,
68                    tls_version_support: TlsVersionSupport::TLS1_2,
69                    extension_order: CipherOrder::FORWARD,
70                },
71                PacketSpecification {
72                    host: host.clone(),
73                    port: port.clone(),
74                    tls_version: TlsVersion::TLS1_2,
75                    cipher_list: CipherList::ALL,
76                    cipher_order: CipherOrder::TOP_HALF,
77                    use_grease: false,
78                    use_rare_apln: false,
79                    tls_version_support: TlsVersionSupport::NO_SUPPORT,
80                    extension_order: CipherOrder::FORWARD,
81                },
82                PacketSpecification {
83                    host: host.clone(),
84                    port: port.clone(),
85                    tls_version: TlsVersion::TLS1_2,
86                    cipher_list: CipherList::ALL,
87                    cipher_order: CipherOrder::BOTTOM_HALF,
88                    use_grease: false,
89                    use_rare_apln: true,
90                    tls_version_support: TlsVersionSupport::NO_SUPPORT,
91                    extension_order: CipherOrder::FORWARD,
92                },
93                PacketSpecification {
94                    host: host.clone(),
95                    port: port.clone(),
96                    tls_version: TlsVersion::TLS1_2,
97                    cipher_list: CipherList::ALL,
98                    cipher_order: CipherOrder::MIDDLE_OUT,
99                    use_grease: true,
100                    use_rare_apln: true,
101                    tls_version_support: TlsVersionSupport::NO_SUPPORT,
102                    extension_order: CipherOrder::REVERSE,
103                },
104                PacketSpecification {
105                    host: host.clone(),
106                    port: port.clone(),
107                    tls_version: TlsVersion::TLS1_1,
108                    cipher_list: CipherList::ALL,
109                    cipher_order: CipherOrder::FORWARD,
110                    use_grease: false,
111                    use_rare_apln: false,
112                    tls_version_support: TlsVersionSupport::NO_SUPPORT,
113                    extension_order: CipherOrder::FORWARD,
114                },
115                PacketSpecification {
116                    host: host.clone(),
117                    port: port.clone(),
118                    tls_version: TlsVersion::TLS1_3,
119                    cipher_list: CipherList::ALL,
120                    cipher_order: CipherOrder::FORWARD,
121                    use_grease: false,
122                    use_rare_apln: false,
123                    tls_version_support: TlsVersionSupport::TLS1_3,
124                    extension_order: CipherOrder::REVERSE,
125                },
126                PacketSpecification {
127                    host: host.clone(),
128                    port: port.clone(),
129                    tls_version: TlsVersion::TLS1_3,
130                    cipher_list: CipherList::ALL,
131                    cipher_order: CipherOrder::REVERSE,
132                    use_grease: false,
133                    use_rare_apln: false,
134                    tls_version_support: TlsVersionSupport::TLS1_3,
135                    extension_order: CipherOrder::FORWARD,
136                },
137                PacketSpecification {
138                    host: host.clone(),
139                    port: port.clone(),
140                    tls_version: TlsVersion::TLS1_3,
141                    cipher_list: CipherList::NO1_3,
142                    cipher_order: CipherOrder::FORWARD,
143                    use_grease: false,
144                    use_rare_apln: false,
145                    tls_version_support: TlsVersionSupport::TLS1_3,
146                    extension_order: CipherOrder::FORWARD,
147                },
148                PacketSpecification {
149                    host,
150                    port,
151                    tls_version: TlsVersion::TLS1_3,
152                    cipher_list: CipherList::ALL,
153                    cipher_order: CipherOrder::MIDDLE_OUT,
154                    use_grease: true,
155                    use_rare_apln: false,
156                    tls_version_support: TlsVersionSupport::TLS1_3,
157                    extension_order: CipherOrder::REVERSE,
158                },
159                //</editor-fold>
160            ],
161            rng: Box::new(PseudoRng {}),
162            timeout: DEFAULT_TIMEOUT
163        }
164    }
165
166    pub fn retrieve_parts(&mut self) -> Result<Vec<JarmPart>, JarmError> {
167        let mut parts = Vec::new();
168        for spec in &self.queue {
169            let payload = build_packet(spec, self.rng.as_ref());
170
171            // Send packet
172            let url = format!("{}:{}", spec.host, spec.port);
173            let address = resolve(url)?;  // Resolve the ip if needed
174            let mut data = [0_u8; SOCKET_BUFFER as usize];
175            match TcpStream::connect_timeout(&address, self.timeout) {
176                Ok(mut stream) => {
177                    stream.set_read_timeout(Some(self.timeout))?;
178                    stream.set_write_timeout(Some(self.timeout))?;
179                    stream.write_all(&payload)?;
180                    let mut handle = stream.take(SOCKET_BUFFER);
181                    let _read_result = handle.read(&mut data)?;
182                },
183                Err(e) => return Err(JarmError::Connection(DetailedError::from(Box::from(e))))
184            }
185            let jarm_part = read_packet(Vec::from(data));
186            parts.push(jarm_part);
187        }
188        Ok(parts)
189    }
190
191    pub fn hash(&mut self) -> Result<String, JarmError> {
192        if self.parts.is_empty(){
193            self.parts = self.retrieve_parts()?
194        }
195        if self.parts.iter().all(|p| p.raw == "|||") {
196            return Ok("0".repeat(62));
197        }
198
199        let mut fuzzy_hash = String::new();
200        let mut alpns_and_ext = String::new();
201
202        for part in &self.parts {
203            let components: Vec<&str> = part.raw.split('|').collect();
204            // Custom jarm hash includes a fuzzy hash of the ciphers and versions
205            fuzzy_hash.push_str(&cipher_bytes(components[0]));
206            fuzzy_hash.push(version_byte(components[1]));
207            alpns_and_ext.push_str(components[2]);
208            alpns_and_ext.push_str(components[3]);
209        }
210
211        // Custom jarm hash has the sha256 of alpns and extensions added to the end
212        let mut hasher = Sha256::new();
213        hasher.update(alpns_and_ext.into_bytes());
214        let sha256 = hex::encode(hasher.finalize());
215        fuzzy_hash.push_str(sha256.get(0..32).unwrap());
216        Ok(fuzzy_hash)
217    }
218}
219
220#[derive(PartialEq, Eq)]
221pub enum TlsVersion {
222    TLS1_1,
223    TLS1_2,
224    TLS1_3,
225}
226
227#[derive(PartialEq, Eq)]
228pub enum CipherList {
229    ALL,
230    NO1_3,
231}
232
233#[allow(non_camel_case_types)]
234#[derive(PartialEq, Eq)]
235pub enum CipherOrder {
236    FORWARD,
237    REVERSE,
238    TOP_HALF,
239    BOTTOM_HALF,
240    MIDDLE_OUT,
241}
242
243#[allow(non_camel_case_types)]
244#[derive(PartialEq, Eq)]
245pub enum TlsVersionSupport {
246    TLS1_2,
247    TLS1_3,
248    NO_SUPPORT,
249}
250
251pub struct PacketSpecification {
252    pub host: String,
253    pub port: String,
254    pub tls_version: TlsVersion,
255    pub cipher_list: CipherList,
256    pub cipher_order: CipherOrder,
257    pub use_grease: bool,
258    pub use_rare_apln: bool,
259    pub tls_version_support: TlsVersionSupport,
260    pub extension_order: CipherOrder,
261}
262
263
264pub fn build_packet(jarm_details: &PacketSpecification, rng: &dyn JarmRng) -> Vec<u8> {
265    let mut client_hello = Vec::new();
266    let mut payload= b"\x16".to_vec();
267
268    match jarm_details.tls_version {
269        TlsVersion::TLS1_1 => {
270            payload.extend(b"\x03\x02");
271            client_hello.extend(b"\x03\x02");
272        }
273        TlsVersion::TLS1_2 => {
274            payload.extend(b"\x03\x03");
275            client_hello.extend(b"\x03\x03");
276        }
277        TlsVersion::TLS1_3 => {
278            payload.extend(b"\x03\x01");
279            client_hello.extend(b"\x03\x03");
280        }
281    }
282
283    client_hello.extend(rng.random_bytes());
284    let session_id = rng.random_bytes();
285    let session_id_length = pack_as_unsigned_char(session_id.len());
286    client_hello.push(session_id_length);
287    client_hello.extend(session_id);
288
289    let cipher_choice = get_ciphers(jarm_details, rng);
290    let client_suites_length = pack_as_unsigned_short(cipher_choice.len());
291    client_hello.extend(client_suites_length);
292    client_hello.extend(cipher_choice);
293    client_hello.push(b'\x01');  // cipher methods
294    client_hello.push(b'\x00');  // compression_methods
295    client_hello.extend(get_extensions(jarm_details, rng));
296
297    // Finish packet assembly
298    let mut inner_length = b"\x00".to_vec();
299    inner_length.extend(pack_as_unsigned_short(client_hello.len()));
300    let mut handshake_protocol = b"\x01".to_vec();
301    handshake_protocol.extend(inner_length);
302    handshake_protocol.extend(client_hello);
303    let outer_length = pack_as_unsigned_short(handshake_protocol.len());
304    payload.extend(outer_length);
305    payload.extend(handshake_protocol);
306    payload
307}
308
309pub fn pack_as_unsigned_char(n: usize) -> u8 {
310    if n >= 256 {
311        panic!("Can't pack_as_unsigned_char {n:?} as it is over 255")
312    }
313    n as u8
314}
315
316pub fn pack_as_unsigned_short(n: usize) -> Vec<u8> {
317    vec![(n >> 8) as u8, n as u8]
318}
319
320pub fn get_ciphers(jarm_details: &PacketSpecification, rng: &dyn JarmRng) -> Vec<u8> {
321    let mut selected_ciphers = Vec::new();
322
323    let mut list = match jarm_details.cipher_list {
324        CipherList::ALL => {
325            vec![b"\x00\x16".to_vec(), b"\x00\x33".to_vec(), b"\x00\x67".to_vec(), b"\xc0\x9e".to_vec(), b"\xc0\xa2".to_vec(), b"\x00\x9e".to_vec(), b"\x00\x39".to_vec(), b"\x00\x6b".to_vec(),
326                b"\xc0\x9f".to_vec(), b"\xc0\xa3".to_vec(), b"\x00\x9f".to_vec(), b"\x00\x45".to_vec(), b"\x00\xbe".to_vec(), b"\x00\x88".to_vec(), b"\x00\xc4".to_vec(), b"\x00\x9a".to_vec(),
327                b"\xc0\x08".to_vec(), b"\xc0\x09".to_vec(), b"\xc0\x23".to_vec(), b"\xc0\xac".to_vec(), b"\xc0\xae".to_vec(), b"\xc0\x2b".to_vec(), b"\xc0\x0a".to_vec(), b"\xc0\x24".to_vec(),
328                b"\xc0\xad".to_vec(), b"\xc0\xaf".to_vec(), b"\xc0\x2c".to_vec(), b"\xc0\x72".to_vec(), b"\xc0\x73".to_vec(), b"\xcc\xa9".to_vec(), b"\x13\x02".to_vec(), b"\x13\x01".to_vec(),
329                b"\xcc\x14".to_vec(), b"\xc0\x07".to_vec(), b"\xc0\x12".to_vec(), b"\xc0\x13".to_vec(), b"\xc0\x27".to_vec(), b"\xc0\x2f".to_vec(), b"\xc0\x14".to_vec(), b"\xc0\x28".to_vec(),
330                b"\xc0\x30".to_vec(), b"\xc0\x60".to_vec(), b"\xc0\x61".to_vec(), b"\xc0\x76".to_vec(), b"\xc0\x77".to_vec(), b"\xcc\xa8".to_vec(), b"\x13\x05".to_vec(), b"\x13\x04".to_vec(),
331                b"\x13\x03".to_vec(), b"\xcc\x13".to_vec(), b"\xc0\x11".to_vec(), b"\x00\x0a".to_vec(), b"\x00\x2f".to_vec(), b"\x00\x3c".to_vec(), b"\xc0\x9c".to_vec(), b"\xc0\xa0".to_vec(),
332                b"\x00\x9c".to_vec(), b"\x00\x35".to_vec(), b"\x00\x3d".to_vec(), b"\xc0\x9d".to_vec(), b"\xc0\xa1".to_vec(), b"\x00\x9d".to_vec(), b"\x00\x41".to_vec(), b"\x00\xba".to_vec(),
333                b"\x00\x84".to_vec(), b"\x00\xc0".to_vec(), b"\x00\x07".to_vec(), b"\x00\x04".to_vec(), b"\x00\x05".to_vec(),
334            ]
335        }
336        CipherList::NO1_3 => {
337            vec![b"\x00\x16".to_vec(), b"\x00\x33".to_vec(), b"\x00\x67".to_vec(), b"\xc0\x9e".to_vec(), b"\xc0\xa2".to_vec(), b"\x00\x9e".to_vec(), b"\x00\x39".to_vec(), b"\x00\x6b".to_vec(),
338                b"\xc0\x9f".to_vec(), b"\xc0\xa3".to_vec(), b"\x00\x9f".to_vec(), b"\x00\x45".to_vec(), b"\x00\xbe".to_vec(), b"\x00\x88".to_vec(), b"\x00\xc4".to_vec(), b"\x00\x9a".to_vec(),
339                b"\xc0\x08".to_vec(), b"\xc0\x09".to_vec(), b"\xc0\x23".to_vec(), b"\xc0\xac".to_vec(), b"\xc0\xae".to_vec(), b"\xc0\x2b".to_vec(), b"\xc0\x0a".to_vec(), b"\xc0\x24".to_vec(),
340                b"\xc0\xad".to_vec(), b"\xc0\xaf".to_vec(), b"\xc0\x2c".to_vec(), b"\xc0\x72".to_vec(), b"\xc0\x73".to_vec(), b"\xcc\xa9".to_vec(), b"\xcc\x14".to_vec(), b"\xc0\x07".to_vec(),
341                b"\xc0\x12".to_vec(), b"\xc0\x13".to_vec(), b"\xc0\x27".to_vec(), b"\xc0\x2f".to_vec(), b"\xc0\x14".to_vec(), b"\xc0\x28".to_vec(), b"\xc0\x30".to_vec(), b"\xc0\x60".to_vec(),
342                b"\xc0\x61".to_vec(), b"\xc0\x76".to_vec(), b"\xc0\x77".to_vec(), b"\xcc\xa8".to_vec(), b"\xcc\x13".to_vec(), b"\xc0\x11".to_vec(), b"\x00\x0a".to_vec(), b"\x00\x2f".to_vec(),
343                b"\x00\x3c".to_vec(), b"\xc0\x9c".to_vec(), b"\xc0\xa0".to_vec(), b"\x00\x9c".to_vec(), b"\x00\x35".to_vec(), b"\x00\x3d".to_vec(), b"\xc0\x9d".to_vec(), b"\xc0\xa1".to_vec(),
344                b"\x00\x9d".to_vec(), b"\x00\x41".to_vec(), b"\x00\xba".to_vec(), b"\x00\x84".to_vec(), b"\x00\xc0".to_vec(), b"\x00\x07".to_vec(), b"\x00\x04".to_vec(), b"\x00\x05".to_vec(),
345            ]
346        }
347    };
348
349    cipher_mung(&mut list, &jarm_details.cipher_order);
350    if jarm_details.use_grease {
351        list.insert(0, rng.random_grease());
352    }
353
354    for x in list {
355        selected_ciphers.extend(x);
356    }
357    selected_ciphers
358}
359
360pub fn get_extensions(jarm_details: &PacketSpecification, rng: &dyn JarmRng) -> Vec<u8> {
361    let mut extension_bytes = Vec::new();
362    let mut all_extensions = Vec::new();
363
364    if jarm_details.use_grease {
365        all_extensions.extend(rng.random_grease());
366        all_extensions.extend(b"\x00\x00");
367    }
368    all_extensions.extend(extension_server_name(jarm_details));
369
370    // Other extensions
371    let extended_master_secret = b"\x00\x17\x00\x00";
372    all_extensions.extend(extended_master_secret);
373    let max_fragment_length = b"\x00\x01\x00\x01\x01";
374    all_extensions.extend(max_fragment_length);
375    let renegotiation_info = b"\xff\x01\x00\x01\x00";
376    all_extensions.extend(renegotiation_info);
377    let supported_groups = b"\x00\x0a\x00\x0a\x00\x08\x00\x1d\x00\x17\x00\x18\x00\x19";
378    all_extensions.extend(supported_groups);
379    let ec_point_formats = b"\x00\x0b\x00\x02\x01\x00";
380    all_extensions.extend(ec_point_formats);
381    let session_ticket = b"\x00\x23\x00\x00";
382    all_extensions.extend(session_ticket);
383
384    // Application Layer Protocol Negotiation extension
385    all_extensions.extend(aplns(jarm_details));
386    let signature_algorithms = b"\x00\x0d\x00\x14\x00\x12\x04\x03\x08\x04\x04\x01\x05\x03\x08\x05\x05\x01\x08\x06\x06\x01\x02\x01";
387    all_extensions.extend(signature_algorithms);
388
389    // Key share extension
390    all_extensions.extend(key_share(jarm_details.use_grease, rng));
391    let psk_key_exchange_modes = b"\x00\x2d\x00\x02\x01\x01";
392    all_extensions.extend(psk_key_exchange_modes);
393
394    if jarm_details.tls_version == TlsVersion::TLS1_3
395        || jarm_details.tls_version_support == TlsVersionSupport::TLS1_2 {
396        all_extensions.extend(supported_versions(jarm_details, rng));
397    }
398
399    extension_bytes.extend(pack_as_unsigned_short(all_extensions.len()));
400    extension_bytes.extend(all_extensions);
401    extension_bytes
402}
403
404pub fn extension_server_name(jarm_details: &PacketSpecification) -> Vec<u8> {
405    let mut ext_sni = b"\x00\x00".to_vec();
406    let host_length = jarm_details.host.len();
407    let ext_sni_length = host_length + 5;
408    ext_sni.extend(pack_as_unsigned_short(ext_sni_length));
409
410    let ext_sni_length2 = host_length + 3;
411    ext_sni.extend(pack_as_unsigned_short(ext_sni_length2));
412    ext_sni.push(b'\x00');
413
414    let ext_sni_length3 = host_length;
415    ext_sni.extend(pack_as_unsigned_short(ext_sni_length3));
416
417    ext_sni.extend(jarm_details.host.bytes());
418    ext_sni
419}
420
421// Client hello apln extension
422pub fn aplns(jarm_details: &PacketSpecification) -> Vec<u8> {
423    let mut ext = b"\x00\x10".to_vec();
424    let mut alpns: Vec<Vec<u8>> = if jarm_details.use_rare_apln {
425        vec![
426            b"\x08\x68\x74\x74\x70\x2f\x30\x2e\x39".to_vec(),
427            b"\x08\x68\x74\x74\x70\x2f\x31\x2e\x30".to_vec(),
428            b"\x06\x73\x70\x64\x79\x2f\x31".to_vec(),
429            b"\x06\x73\x70\x64\x79\x2f\x32".to_vec(),
430            b"\x06\x73\x70\x64\x79\x2f\x33".to_vec(),
431            b"\x03\x68\x32\x63".to_vec(),
432            b"\x02\x68\x71".to_vec(),
433        ]
434    } else {
435        vec![
436            b"\x08\x68\x74\x74\x70\x2f\x30\x2e\x39".to_vec(),
437            b"\x08\x68\x74\x74\x70\x2f\x31\x2e\x30".to_vec(),
438            b"\x08\x68\x74\x74\x70\x2f\x31\x2e\x31".to_vec(),
439            b"\x06\x73\x70\x64\x79\x2f\x31".to_vec(),
440            b"\x06\x73\x70\x64\x79\x2f\x32".to_vec(),
441            b"\x06\x73\x70\x64\x79\x2f\x33\x02\x68\x32".to_vec(),
442            b"\x03\x68\x32\x63".to_vec(),
443            b"\x02\x68\x71".to_vec()
444        ]
445    };
446
447    cipher_mung(&mut alpns, &jarm_details.extension_order);
448
449    // flatten the alpns
450    let mut all_alpns = Vec::new();
451    for alpn in alpns {
452        all_alpns.extend(alpn);
453    }
454
455    let second_length  = all_alpns.len();
456    let first_length = second_length + 2;
457    ext.extend(pack_as_unsigned_short(first_length));
458    ext.extend(pack_as_unsigned_short(second_length));
459    ext.extend(all_alpns);
460    ext
461}
462
463pub fn cipher_mung(ciphers: &mut Vec<Vec<u8>>, cipher_order: &CipherOrder) {
464    match cipher_order {
465        CipherOrder::FORWARD => {}  // nothing to do
466        CipherOrder::REVERSE => { ciphers.reverse() }
467        CipherOrder::TOP_HALF => {
468            // Top half gets the middle cipher if needed
469            let middle_one = if ciphers.len() % 2 == 1 {
470                Some(ciphers[ciphers.len() / 2].clone())
471            } else {
472                None
473            };
474            cipher_mung(ciphers, &CipherOrder::REVERSE);
475            cipher_mung(ciphers, &CipherOrder::BOTTOM_HALF);
476
477            if let Some(x) = middle_one {
478                ciphers.insert(0, x);
479            }
480        }
481        CipherOrder::BOTTOM_HALF => {
482            let mut range_to_drain = 0..ciphers.len() / 2;
483            if ciphers.len() % 2 == 1 {
484                // Also remove the middle one if the length is odd
485                range_to_drain.end += 1;
486            }
487            ciphers.drain(range_to_drain);
488
489        }
490        CipherOrder::MIDDLE_OUT => {
491            let middle = ciphers.len() / 2;
492            let mut output = Vec::new();
493            if ciphers.len() % 2 == 1 {
494                // output.append(ciphers[middle])
495                output.push(ciphers[middle].clone());
496
497                for i in 1..middle+1 {
498                    output.push(ciphers[middle + i].clone());
499                    output.push(ciphers[middle - i].clone());
500                }
501            } else {
502                for i in 1..middle+1 {
503                    output.push(ciphers[middle - 1 + i].clone());
504                    output.push(ciphers[middle - i].clone());
505                }
506            }
507            *ciphers = output;
508        }
509    }
510}
511
512pub fn key_share(grease: bool, rng: &dyn JarmRng) -> Vec<u8> {
513    let mut ext = b"\x00\x33".to_vec();
514
515    let mut share_ext = if grease {
516        let mut grease_start = rng.random_grease();
517        grease_start.extend(b"\x00\x01\x00");
518        grease_start
519    } else {
520        Vec::new()
521    };
522    share_ext.extend(b"\x00\x1d");  // group
523    share_ext.extend(b"\x00\x20");  // key_exchange_length
524    share_ext.extend(rng.random_bytes());  // key_exchange_length
525
526    let second_length  = share_ext.len();
527    let first_length = second_length + 2;
528    ext.extend(pack_as_unsigned_short(first_length));
529    ext.extend(pack_as_unsigned_short(second_length));
530    ext.extend(share_ext);
531    ext
532}
533
534pub fn supported_versions(jarm_details: &PacketSpecification, rng: &dyn JarmRng) -> Vec<u8> {
535    let mut tls = if jarm_details.tls_version_support == TlsVersionSupport::TLS1_2 {
536        vec![b"\x03\x01".to_vec(), b"\x03\x02".to_vec(), b"\x03\x03".to_vec()]
537    } else {  // TLS 1.3 is supported
538        vec![b"\x03\x01".to_vec(), b"\x03\x02".to_vec(), b"\x03\x03".to_vec(), b"\x03\x04".to_vec()]
539    };
540    cipher_mung(&mut tls, &jarm_details.extension_order);
541
542    // Assemble the extension
543    let mut ext = b"\x00\x2b".to_vec();
544    let mut versions = if jarm_details.use_grease {
545        rng.random_grease()
546    } else {
547        Vec::new()
548    };
549
550    for version in tls {
551        versions.extend(version);
552    }
553
554    let second_length  = versions.len();
555    let first_length = second_length + 1;
556    ext.extend(pack_as_unsigned_short(first_length));
557    ext.push(pack_as_unsigned_char(second_length));
558    ext.extend(versions);
559    ext
560}
561
562pub fn read_packet(data: Vec<u8>) -> JarmPart {
563    if (data[0] != 22) || (data[5] != 2){
564        return JarmPart::new("|||");  // Default jarm
565    }
566
567    let mut jarm = String::new();
568    let counter = data[43] as usize;
569
570    // Find server's selected cipher
571    let start = counter + 44;
572    let end = counter + 45;
573    let selected_cipher = &data[start..=end];
574
575    // Find server's selected version
576    let version = &data[9..=10];
577
578    // Format
579    jarm += &*hex::encode(selected_cipher);
580    jarm += "|";
581    jarm += &*hex::encode(version);
582    jarm += "|";
583
584    // Extract extensions
585    let extensions = extract_extension_info(data, counter);
586    jarm += &*extensions;
587    JarmPart { raw: jarm}
588}
589
590// Convert bytes array to u32
591pub fn as_u32_be(array: &[u8]) -> u32 {
592    if array.len() != 2 {
593        eprintln!("array = {array:?}");
594        unimplemented!()  // not needed for now
595    }
596    ((array[0] as u32) << 8) + (array[1] as u32)
597}
598
599pub fn extract_extension_info(data: Vec<u8>, counter: usize) -> String {
600    // Error handling
601    if data_has_errors(&data, counter) {
602        return "|".to_string();
603    }
604
605    // Collect types and value
606    let mut count = 49 + (counter as u32);
607    let length_start = counter + 47;
608    let length_end = counter + 48;
609
610    let length_slice: &[u8] = &data[length_start..=length_end];
611    let length = as_u32_be(length_slice);
612    let maximum = length + (count - 1);
613
614    let mut types: Vec<&[u8]> = Vec::new();
615    let mut values: Vec<Option<&[u8]>> = Vec::new();
616
617    while count < maximum {
618        let slice_start = count as usize;
619        types.push(&data[slice_start..slice_start+2]);
620
621        let ext_length_start = (count + 2) as usize;
622        let ext_length_end = ext_length_start + 2;
623        let ext_length_slice: &[u8] = &data[ext_length_start..ext_length_end];
624        let ext_length = as_u32_be(ext_length_slice);
625
626        if ext_length == 0 {
627            values.push(None);  // TODO FIXME
628            count += 4;
629        } else {
630            let value = &data[slice_start + 4..slice_start + 4 + ext_length as usize];
631            values.push(Some(value));
632            count += ext_length + 4
633        }
634    }
635
636    // Read application_layer_protocol_negotiation
637    let alpn = find_extension(&types, values);
638
639    let formatted_types = add_formatting_hyphen(&types);
640    format!("{alpn}|{formatted_types}")
641}
642
643fn data_has_errors(data: &[u8], counter: usize) -> bool {
644    let length_start = counter + 47;
645    if data[length_start] == 11 {
646        return true;
647    }
648    if data[(counter + 50)..(counter + 53)] == b"\x0e\xac\x0b".to_vec() ||
649        data[(counter + 82)..(counter + 85)] == b"\x0f\xf0\x0b".to_vec() {
650        return true;
651    }
652    let server_hello_length_slice: &[u8] = &data[3..5];
653    let server_hello_length = as_u32_be(server_hello_length_slice);
654    if (counter as u32) + 42 >= server_hello_length {
655        return true;
656    }
657    false
658}
659
660pub fn add_formatting_hyphen(types: &[&[u8]]) -> String {
661    let types_hex_encoded: Vec<String> = types.iter().map(hex::encode).collect();
662    types_hex_encoded.join("-")
663}
664
665
666pub fn find_extension(types: &[&[u8]], values: Vec<Option<&[u8]>>) -> String {
667    let mut i = 0;
668    while i < types.len() {
669        if types.get(i).unwrap() == ALPN_EXTENSION {
670            let x = values.get(i).unwrap();
671            match x {
672                None => {}
673                Some(y) => {
674                    match std::str::from_utf8(&y[3..]) {
675                        Ok(s) => return s.to_string(),
676                        Err(e) => panic!("Invalid UTF-8 sequence: {e}"),
677                    };
678                }
679            }
680        }
681        i += 1
682    }
683    "".to_string()
684}
685
686pub fn cipher_bytes(cipher: &str) -> String {
687    if cipher.is_empty() {
688        return "00".to_string()
689    }
690
691    let list = vec![
692        b"\x00\x04", b"\x00\x05", b"\x00\x07", b"\x00\x0a", b"\x00\x16", b"\x00\x2f", b"\x00\x33", b"\x00\x35",
693        b"\x00\x39", b"\x00\x3c", b"\x00\x3d", b"\x00\x41", b"\x00\x45", b"\x00\x67", b"\x00\x6b", b"\x00\x84",
694        b"\x00\x88", b"\x00\x9a", b"\x00\x9c", b"\x00\x9d", b"\x00\x9e", b"\x00\x9f", b"\x00\xba", b"\x00\xbe",
695        b"\x00\xc0", b"\x00\xc4", b"\xc0\x07", b"\xc0\x08", b"\xc0\x09", b"\xc0\x0a", b"\xc0\x11", b"\xc0\x12",
696        b"\xc0\x13", b"\xc0\x14", b"\xc0\x23", b"\xc0\x24", b"\xc0\x27", b"\xc0\x28", b"\xc0\x2b", b"\xc0\x2c",
697        b"\xc0\x2f", b"\xc0\x30", b"\xc0\x60", b"\xc0\x61", b"\xc0\x72", b"\xc0\x73", b"\xc0\x76", b"\xc0\x77",
698        b"\xc0\x9c", b"\xc0\x9d", b"\xc0\x9e", b"\xc0\x9f", b"\xc0\xa0", b"\xc0\xa1", b"\xc0\xa2", b"\xc0\xa3",
699        b"\xc0\xac", b"\xc0\xad", b"\xc0\xae", b"\xc0\xaf", b"\xcc\x13", b"\xcc\x14", b"\xcc\xa8", b"\xcc\xa9",
700        b"\x13\x01", b"\x13\x02", b"\x13\x03", b"\x13\x04", b"\x13\x05"
701    ];
702    let count = match list.iter().position(|&bytes| hex::encode(bytes) == cipher) {
703        None => { list.len() + 1 }
704        Some(index) => { index + 1 }
705    };
706
707    let hex_value = hex::encode(count.to_be_bytes());
708    hex_value.get(hex_value.len() - 2..hex_value.len()).unwrap().to_string()
709}
710
711pub fn version_byte(version: &str) -> char {
712    if version.is_empty() {
713        return '0';
714    }
715    let option = "abcdef".to_string();
716    let version_index: usize = 3;
717    let count: usize = match version.get(version_index..version_index+1) {
718        None => { panic!("version not expected {version:?}")}
719        Some(str_count) => { usize::from_str(str_count).unwrap() }
720    };
721    option.chars().nth(count).unwrap()
722}
723
724
725/// Resolve the given url to an ip
726/// the first ip found is returned, else an error is raised.
727fn resolve(url: String) -> Result<SocketAddr, JarmError> {
728    let mut ips = match url.to_socket_addrs() {
729        Ok(address) => address,
730        Err(e) => {
731            let error = DetailedError::from(Box::from(e));
732            return Err(JarmError::DnsResolve(error))
733        },
734    };
735    if let Some(address) = ips.next() {
736        Ok(address)
737    } else {
738        Err(JarmError::DnsResolve(DetailedError::default()))
739    }
740}
741
742
743pub trait JarmRng {
744    fn random_bytes(&self) -> Vec<u8>;
745
746    fn random_grease(&self) -> Vec<u8>;
747}
748
749pub struct PseudoRng {}
750
751pub struct TestRng {}
752
753impl JarmRng for TestRng {  // Mocked Rng used in tests
754    fn random_bytes(&self) -> Vec<u8> {
755        vec![42; 32]
756    }
757
758    fn random_grease(&self) -> Vec<u8> {
759        b"\x0a\x0a".to_vec()
760    }
761}
762
763#[cfg(not(tarpaulin_include))]  // disable coverage
764impl JarmRng for PseudoRng {  // Real Rng used outside of tests
765    fn random_bytes(&self) -> Vec<u8> {
766        let mut rng = rand::rng();
767        rng.random::<[u8; 32]>().to_vec()
768    }
769
770    fn random_grease(&self) -> Vec<u8> {
771        let grease_list = vec![
772            b"\x0a\x0a".to_vec(),
773            b"\x1a\x1a".to_vec(),
774            b"\x2a\x2a".to_vec(),
775            b"\x3a\x3a".to_vec(),
776            b"\x4a\x4a".to_vec(),
777            b"\x5a\x5a".to_vec(),
778            b"\x6a\x6a".to_vec(),
779            b"\x7a\x7a".to_vec(),
780            b"\x8a\x8a".to_vec(),
781            b"\x9a\x9a".to_vec(),
782            b"\xaa\xaa".to_vec(),
783            b"\xba\xba".to_vec(),
784            b"\xca\xca".to_vec(),
785            b"\xda\xda".to_vec(),
786            b"\xea\xea".to_vec(),
787            b"\xfa\xfa".to_vec(),
788        ];
789        grease_list.choose(&mut rand::rng()).unwrap().clone()
790    }
791}
792
793#[cfg(test)]
794mod tests {
795    use rstest::*;
796    use crate::resolve;
797    use crate::error::JarmError;
798
799    #[rstest]
800    #[case("invalid_url")]
801    #[case("google.com")]  // missing port
802    fn test_dns_resolve_error(#[case] invalid_url: String) {
803        let expected_error = "invalid socket address";
804        let error = resolve(invalid_url).err().unwrap();
805        if let JarmError::DnsResolve(err) = error {
806            let underlying_error = err.underlying_error.unwrap();
807            assert_eq!(underlying_error.to_string(), expected_error);
808        } else { panic!("unexpected type") }
809    }
810}