assemblyline_models/types/
ssdeep.rs

1use serde_with::{DeserializeFromStr, SerializeDisplay};
2use struct_metadata::Described;
3
4use crate::{ElasticMeta, ModelError};
5
6
7
8/// Validated ssdeep type
9#[derive(SerializeDisplay, DeserializeFromStr, Described, PartialEq, Eq, Debug, Clone)]
10#[metadata_type(ElasticMeta)]
11#[metadata(mapping="text", analyzer="text_fuzzy")]
12pub struct SSDeepHash(String);
13
14impl std::fmt::Display for SSDeepHash {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        f.write_str(&self.0)
17    }
18}
19
20pub fn is_ssdeep_char(value: char) -> bool {
21    value.is_ascii_alphanumeric() || value == '/' || value == '+'
22}
23
24pub fn is_ssdeep_hash(value: &str) -> bool {
25    // SSDEEP_REGEX = r"^[0-9]{1,18}:[a-zA-Z0-9/+]{0,64}:[a-zA-Z0-9/+]{0,64}$"
26    let (numbers, hashes) = match value.split_once(":") {
27        Some(value) => value,
28        None => return false
29    };
30    let (hasha, hashb) = match hashes.split_once(":") {
31        Some(value) => value,
32        None => return false
33    };
34    if numbers.is_empty() || numbers.len() > 18 || numbers.chars().any(|c|!c.is_ascii_digit()) {
35        return false
36    }
37    if hasha.len() > 64 || hasha.chars().any(|c|!is_ssdeep_char(c)) {
38        return false
39    }
40    if hashb.len() > 64 || hashb.chars().any(|c|!is_ssdeep_char(c)) {
41        return false
42    }
43    return true
44}
45
46impl std::str::FromStr for SSDeepHash {
47    type Err = ModelError;
48
49    fn from_str(s: &str) -> Result<Self, Self::Err> {
50        if is_ssdeep_hash(s) {
51            Ok(SSDeepHash(s.to_owned()))
52        } else {
53            Err(ModelError::InvalidSSDeep(s.to_owned()))
54        }
55    }
56}
57
58#[cfg(feature = "rand")]
59impl rand::distr::Distribution<SSDeepHash> for rand::distr::StandardUniform {
60    fn sample<R: rand::prelude::Rng + ?Sized>(&self, rng: &mut R) -> SSDeepHash {
61        use rand::distr::{Alphanumeric, SampleString};
62        let mut output = String::new();
63        output += &rng.random_range(0..10000).to_string();
64        output += ":";
65        let len = rng.random_range(0..64);
66        output += &Alphanumeric.sample_string(rng, len);
67        output += ":";
68        let len = rng.random_range(0..64);
69        output += &Alphanumeric.sample_string(rng, len);
70        SSDeepHash(output)
71    }
72}