Skip to main content

leyline/audit/
mod.rs

1#![forbid(unsafe_code)]
2mod compare;
3mod ja3;
4mod ja4;
5mod ja4h;
6mod ja4t;
7
8pub use compare::{FieldOutcome, FingerprintReport, HeaderOutcome, Observed};
9pub use ja3::{Ja3Input, compute_ja3};
10pub use ja4::{Ja4Input, compute_ja4};
11pub use ja4h::{Ja4hInput, compute_ja4h};
12pub use ja4t::compute_ja4t;
13
14use sha2::{Digest, Sha256};
15
16use crate::iana::{cipher_id, curve_id, is_grease};
17
18fn non_grease_cipher_ids(ciphers: &[String]) -> Vec<u16> {
19    ciphers
20        .iter()
21        .filter_map(|c| cipher_id(c))
22        .filter(|id| !is_grease(*id))
23        .collect()
24}
25
26fn non_grease_ext_ids(extension_ids: &[u16]) -> Vec<u16> {
27    extension_ids
28        .iter()
29        .copied()
30        .filter(|id| !is_grease(*id))
31        .collect()
32}
33
34fn non_grease_curve_ids(curves: &[String]) -> Vec<u16> {
35    curves
36        .iter()
37        .filter_map(|c| curve_id(c))
38        .filter(|id| !is_grease(*id))
39        .collect()
40}
41
42fn hash12(s: &str) -> String {
43    if s.is_empty() {
44        "000000000000".to_owned()
45    } else {
46        let digest = Sha256::digest(s.as_bytes());
47        hex::encode(digest)[..12].to_owned()
48    }
49}
50
51#[derive(Debug, Clone)]
52#[non_exhaustive]
53pub struct AuditData {
54    pub ja4: String,
55    pub ja3: String,
56    pub h2_fingerprint: String,
57    pub ja4t: String,
58    pub ja4h: String,
59    pub permutes_extensions: bool,
60    pub request_headers: Vec<(String, String)>,
61}
62
63#[derive(Debug, Clone)]
64pub(crate) struct AuditTlsCache {
65    pub(crate) ja4: String,
66    pub(crate) ja3: String,
67    pub(crate) h2_fingerprint: String,
68    pub(crate) ja4t: String,
69    pub(crate) permutes_extensions: bool,
70}