leyline-http 0.1.5

HTTP client that impersonates Chrome, Firefox, and Safari on the wire: TLS (JA3/JA4), HTTP/2, and HTTP/3 fingerprints
Documentation
use crate::audit::{non_grease_cipher_ids, non_grease_curve_ids, non_grease_ext_ids};

#[derive(Debug)]
pub struct Ja3Input<'a> {
    pub ciphers: &'a [String],
    pub curves: &'a [String],
    pub extension_ids: &'a [u16],
    pub tls_record_version: u16,
}

pub fn compute_ja3(input: &Ja3Input<'_>) -> String {
    let raw = input.compute_ja3_raw();
    use md5::Digest as _;
    format!("{:x}", md5::Md5::digest(raw.as_bytes()))
}

impl Ja3Input<'_> {
    pub(crate) fn compute_ja3_raw(&self) -> String {
        let version = self.tls_record_version;

        let ciphers: String = non_grease_cipher_ids(self.ciphers)
            .iter()
            .map(|id| id.to_string())
            .collect::<Vec<_>>()
            .join("-");

        let extensions: String = non_grease_ext_ids(self.extension_ids)
            .iter()
            .map(|id| id.to_string())
            .collect::<Vec<_>>()
            .join("-");

        let curves: String = non_grease_curve_ids(self.curves)
            .iter()
            .map(|id| id.to_string())
            .collect::<Vec<_>>()
            .join("-");

        let point_formats = "0";

        format!("{version},{ciphers},{extensions},{curves},{point_formats}")
    }
}

#[cfg(test)]
mod tests;