1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
use crate::AnalyzedPassword;

#[cfg(feature = "common-password")]
#[inline]
fn is_common(analyzed_password: &AnalyzedPassword) -> bool {
    analyzed_password.is_common()
}

#[cfg(not(feature = "common-password"))]
#[inline]
fn is_common(_: &AnalyzedPassword) -> bool {
    false
}

/// Score a password by using its analysis.
/// * 0 ~ 20 is very dangerous (may be cracked within few seconds)
/// * 20 ~ 40 is dangerous
/// * 40 ~ 60 is very weak
/// * 60 ~ 80 is weak
/// * 80 ~ 90 is good
/// * 90 ~ 95 is strong
/// * 95 ~ 99 is very strong
/// * 99 ~ 100 is invulnerable
pub fn score(analyzed_password: &AnalyzedPassword) -> f64 {
    let max_score = match analyzed_password.length() - analyzed_password.other_characters_count() {
        0 => 0f64,
        1 => 2f64,
        2 => 5f64,
        3 => 9f64,
        4 => 16f64,
        5 => 25f64,
        6 => 40f64,
        7 => 58f64,
        8 => 80f64,
        9 => 88f64,
        10 => 95f64,
        11 => 100f64,
        _ => (100 + analyzed_password.length() - 11) as f64,
    };

    let mut score = max_score;

    if score > 0f64 {
        if analyzed_password.spaces_count() >= 1 {
            score += analyzed_password.spaces_count() as f64;
        }

        if analyzed_password.numbers_count() == 0 {
            score -= max_score * 0.05;
        }

        if analyzed_password.lowercase_letters_count() == 0 {
            score -= max_score * 0.1;
        }
        if analyzed_password.uppercase_letters_count() == 0 {
            score -= max_score * 0.1;
        }
        if analyzed_password.spaces_count() == 0 {
            score -= max_score * 0.1;
        }
        if analyzed_password.lowercase_letters_count() >= 1
            && analyzed_password.uppercase_letters_count() >= 1
        {
            score += 1f64;
        }
        if analyzed_password.symbols_count() >= 1 {
            score += 1f64;
        }

        score -= max_score
            * (analyzed_password.consecutive_count() as f64
                / analyzed_password.length() as f64
                / 5f64);

        score -= max_score
            * (analyzed_password.progressive_count() as f64
                / analyzed_password.length() as f64
                / 5f64);

        score -= max_score
            * (analyzed_password.non_consecutive_count() as f64
                / analyzed_password.length() as f64
                / 10f64);
    }

    if score < 0f64 {
        score = 0f64;
    } else if score > max_score {
        score = max_score;
    }

    score += analyzed_password.other_characters_count() as f64 * 20f64;

    if score > 100f64 {
        score = 100f64;
    }

    if is_common(analyzed_password) {
        score /= 5f64;
    }

    score
}