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
|
from decimal import Decimal, Context, Inexact
def estimate_attack_times(guesses):
crack_times_seconds = {
'online_throttling_100_per_hour': Decimal(guesses) / float_to_decimal(100.0 / 3600.0),
'online_no_throttling_10_per_second': Decimal(guesses) / float_to_decimal(10.0),
'offline_slow_hashing_1e4_per_second': Decimal(guesses) / float_to_decimal(1e4),
'offline_fast_hashing_1e10_per_second': Decimal(guesses) / float_to_decimal(1e10),
}
crack_times_display = {}
for scenario, seconds in crack_times_seconds.items():
crack_times_display[scenario] = display_time(seconds)
return {
'crack_times_seconds': crack_times_seconds,
'crack_times_display': crack_times_display,
'score': guesses_to_score(guesses),
}
def guesses_to_score(guesses):
delta = 5
if guesses < 1e3 + delta:
# risky password: "too guessable"
return 0
elif guesses < 1e6 + delta:
# modest protection from throttled online attacks: "very guessable"
return 1
elif guesses < 1e8 + delta:
# modest protection from unthrottled online attacks: "somewhat
# guessable"
return 2
elif guesses < 1e10 + delta:
# modest protection from offline attacks: "safely unguessable"
# assuming a salted, slow hash function like bcrypt, scrypt, PBKDF2,
# argon, etc
return 3
else:
# strong protection from offline attacks under same scenario: "very
# unguessable"
return 4
def display_time(seconds):
minute = 60
hour = minute * 60
day = hour * 24
month = day * 31
year = month * 12
century = year * 100
if seconds < 1:
display_num, display_str = None, 'less than a second'
elif seconds < minute:
base = round(seconds)
display_num, display_str = base, '%s second' % base
elif seconds < hour:
base = round(seconds / minute)
display_num, display_str = base, '%s minute' % base
elif seconds < day:
base = round(seconds / hour)
display_num, display_str = base, '%s hour' % base
elif seconds < month:
base = round(seconds / day)
display_num, display_str = base, '%s day' % base
elif seconds < year:
base = round(seconds / month)
display_num, display_str = base, '%s month' % base
elif seconds < century:
base = round(seconds / year)
display_num, display_str = base, '%s year' % base
else:
display_num, display_str = None, 'centuries'
if display_num and display_num != 1:
display_str += 's'
return display_str
def float_to_decimal(f):
"Convert a floating point number to a Decimal with no loss of information"
n, d = f.as_integer_ratio()
numerator, denominator = Decimal(n), Decimal(d)
ctx = Context(prec=60)
result = ctx.divide(numerator, denominator)
while ctx.flags[Inexact]:
ctx.flags[Inexact] = False
ctx.prec *= 2
result = ctx.divide(numerator, denominator)
return result
|