Initial QSfera import
This commit is contained in:
+104
@@ -0,0 +1,104 @@
|
||||
package zxcvbn
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type EstimatedTimes struct {
|
||||
CrackTimesSeconds map[string]float64 `json:"crack_times_seconds"`
|
||||
CrashTimesDisplay map[string]string `json:"crack_times_display"`
|
||||
Score int
|
||||
}
|
||||
|
||||
func estimateAttackTimes(guesses float64) (t EstimatedTimes) {
|
||||
// crack_times_seconds
|
||||
t.CrackTimesSeconds = make(map[string]float64)
|
||||
t.CrackTimesSeconds["online_throttling_100_per_hour"] = guesses / (100 / 3600)
|
||||
t.CrackTimesSeconds["online_no_throttling_10_per_second"] = guesses / 10
|
||||
t.CrackTimesSeconds["offline_slow_hashing_1e4_per_second"] = guesses / 1e4
|
||||
t.CrackTimesSeconds["offline_fast_hashing_1e10_per_second"] = guesses / 1e10
|
||||
|
||||
t.CrashTimesDisplay = make(map[string]string)
|
||||
|
||||
for scenario, seconds := range t.CrackTimesSeconds {
|
||||
t.CrashTimesDisplay[scenario] = displayTime(seconds)
|
||||
}
|
||||
|
||||
t.Score = guessesToScore(guesses)
|
||||
return
|
||||
}
|
||||
|
||||
func guessesToScore(guesses float64) int {
|
||||
const DELTA = 5
|
||||
if guesses < 1e3+DELTA {
|
||||
// risky password: "too guessable"
|
||||
return 0
|
||||
}
|
||||
if guesses < 1e6+DELTA {
|
||||
// modest protection from throttled online attacks: "very guessable"
|
||||
return 1
|
||||
}
|
||||
if guesses < 1e8+DELTA {
|
||||
// modest protection from unthrottled online attacks: "somewhat guessable"
|
||||
return 2
|
||||
}
|
||||
if guesses < 1e10+DELTA {
|
||||
// modest protection from offline attacks: "safely unguessable"
|
||||
// assuming a salted, slow hash function like bcrypt, scrypt, PBKDF2, argon, etc
|
||||
return 3
|
||||
}
|
||||
// strong protection from offline attacks under same scenario: "very unguessable"
|
||||
return 4
|
||||
}
|
||||
|
||||
func displayTime(seconds float64) string {
|
||||
minute := float64(60)
|
||||
hour := minute * 60
|
||||
day := hour * 24
|
||||
month := day * 31
|
||||
year := month * 12
|
||||
century := year * 100
|
||||
|
||||
if seconds < 1 {
|
||||
return "less than a second"
|
||||
}
|
||||
if seconds < minute {
|
||||
return strCount(seconds, "second")
|
||||
} else if seconds < hour {
|
||||
return strCount(seconds/minute, "minute")
|
||||
} else if seconds < day {
|
||||
return strCount(seconds/hour, "hour")
|
||||
} else if seconds < month {
|
||||
return strCount(seconds/day, "day")
|
||||
} else if seconds < year {
|
||||
return strCount(seconds/month, "month")
|
||||
} else if seconds < century {
|
||||
return strCount(seconds/year, "year")
|
||||
} else {
|
||||
return "centuries"
|
||||
}
|
||||
}
|
||||
|
||||
func strCount(count float64, base string) string {
|
||||
c := int(round(count, 0.5, 0))
|
||||
str := fmt.Sprintf("%d %s", c, base)
|
||||
if c > 1 {
|
||||
str += "s"
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func round(val float64, roundOn float64, places int) (newVal float64) {
|
||||
var round float64
|
||||
pow := math.Pow(10, float64(places))
|
||||
digit := pow * val
|
||||
_, div := math.Modf(digit)
|
||||
if div >= roundOn {
|
||||
round = math.Ceil(digit)
|
||||
} else {
|
||||
round = math.Floor(digit)
|
||||
}
|
||||
newVal = round / pow
|
||||
return
|
||||
}
|
||||
Reference in New Issue
Block a user