Skip to main content

leyline/audit/
ja3.rs

1use crate::audit::{non_grease_cipher_ids, non_grease_curve_ids, non_grease_ext_ids};
2
3#[derive(Debug)]
4pub struct Ja3Input<'a> {
5    pub ciphers: &'a [String],
6    pub curves: &'a [String],
7    pub extension_ids: &'a [u16],
8    pub tls_record_version: u16,
9}
10
11pub fn compute_ja3(input: &Ja3Input<'_>) -> String {
12    let raw = compute_ja3_raw(input);
13    use md5::Digest as _;
14    format!("{:x}", md5::Md5::digest(raw.as_bytes()))
15}
16
17pub(crate) fn compute_ja3_raw(input: &Ja3Input<'_>) -> String {
18    let version = input.tls_record_version;
19
20    let ciphers: String = non_grease_cipher_ids(input.ciphers)
21        .iter()
22        .map(|id| id.to_string())
23        .collect::<Vec<_>>()
24        .join("-");
25
26    let extensions: String = non_grease_ext_ids(input.extension_ids)
27        .iter()
28        .map(|id| id.to_string())
29        .collect::<Vec<_>>()
30        .join("-");
31
32    let curves: String = non_grease_curve_ids(input.curves)
33        .iter()
34        .map(|id| id.to_string())
35        .collect::<Vec<_>>()
36        .join("-");
37
38    let point_formats = "0";
39
40    format!("{version},{ciphers},{extensions},{curves},{point_formats}")
41}
42
43#[cfg(test)]
44mod tests;