mirror of
https://github.com/status-im/zxcvbn-go.git
synced 2025-01-18 23:31:00 +00:00
Fixed spatial Entropy scoring. Added sequence scoring.
This commit is contained in:
parent
80ad1b1d96
commit
a9a908cd1d
@ -16,36 +16,35 @@ type AdjacencyGraph struct {
|
||||
|
||||
var AdjacencyGph = make(map[string]AdjacencyGraph);
|
||||
func init() {
|
||||
//todo get currentloc so that i don't have to know the whole path
|
||||
log.SetFlags(log.Lshortfile)
|
||||
AdjacencyGph["qwerty"] = buildQwerty()
|
||||
AdjacencyGph["dvorak"] = buildDvorak()
|
||||
AdjacencyGph["keypad"] = buildKeypad()
|
||||
AdjacencyGph["macKeypad"] = buildMacKeypad()
|
||||
AdjacencyGph["qwerty"] = BuildQwerty()
|
||||
AdjacencyGph["dvorak"] = BuildDvorak()
|
||||
AdjacencyGph["keypad"] = BuildKeypad()
|
||||
AdjacencyGph["macKeypad"] = BuildMacKeypad()
|
||||
}
|
||||
|
||||
func buildQwerty() AdjacencyGraph {
|
||||
func BuildQwerty() AdjacencyGraph {
|
||||
data, err := zxcvbn_data.Asset("data/Qwerty.json")
|
||||
if err != nil {
|
||||
panic("Can't find asset")
|
||||
}
|
||||
return GetAdjancencyGraphFromFile(data, "qwerty")
|
||||
}
|
||||
func buildDvorak() AdjacencyGraph {
|
||||
func BuildDvorak() AdjacencyGraph {
|
||||
data, err := zxcvbn_data.Asset("data/Dvorak.json")
|
||||
if err != nil {
|
||||
panic("Can't find asset")
|
||||
}
|
||||
return GetAdjancencyGraphFromFile(data, "dvorak")
|
||||
}
|
||||
func buildKeypad() AdjacencyGraph {
|
||||
func BuildKeypad() AdjacencyGraph {
|
||||
data, err := zxcvbn_data.Asset("data/Keypad.json")
|
||||
if err != nil {
|
||||
panic("Can't find asset")
|
||||
}
|
||||
return GetAdjancencyGraphFromFile(data, "keypad")
|
||||
}
|
||||
func buildMacKeypad() AdjacencyGraph {
|
||||
func BuildMacKeypad() AdjacencyGraph {
|
||||
data, err := zxcvbn_data.Asset("data/MacKeypad.json")
|
||||
if err != nil {
|
||||
panic("Can't find asset")
|
||||
|
@ -23,6 +23,8 @@ type Match struct {
|
||||
MatchedWord string
|
||||
Rank float64
|
||||
DictionaryName string
|
||||
DictionaryLength int
|
||||
Ascending bool
|
||||
Turns int
|
||||
ShiftedCount int
|
||||
Entropy float64
|
||||
|
@ -19,6 +19,8 @@ var (
|
||||
KEYPAD_STARTING_POSITIONS int
|
||||
KEYPAD_AVG_DEGREE float64
|
||||
L33T_TABLE adjacency.AdjacencyGraph
|
||||
|
||||
SEQUENCES map[string]string
|
||||
)
|
||||
|
||||
const (
|
||||
@ -29,7 +31,7 @@ const (
|
||||
|
||||
|
||||
func init() {
|
||||
|
||||
loadFrequencyList()
|
||||
}
|
||||
|
||||
func Omnimatch(password string, userInputs []string) (matches []match.Match) {
|
||||
@ -70,9 +72,16 @@ func loadFrequencyList() {
|
||||
// l33tFilePath, _ := filepath.Abs("adjacency/L33t.json")
|
||||
// L33T_TABLE = adjacency.GetAdjancencyGraphFromFile(l33tFilePath, "l33t")
|
||||
|
||||
SEQUENCES = make(map[string]string)
|
||||
SEQUENCES["lower"] = "abcdefghijklmnopqrstuvwxyz"
|
||||
SEQUENCES["upper"] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
SEQUENCES["digits"] = "0123456789"
|
||||
|
||||
MATCHERS = append(MATCHERS, DICTIONARY_MATCHERS...)
|
||||
MATCHERS = append(MATCHERS, spatialMatch)
|
||||
MATCHERS = append(MATCHERS, repeatMatch)
|
||||
MATCHERS = append(MATCHERS, SequenceMatch)
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -379,3 +388,61 @@ func repeatMatch(password string) []match.Match {
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
func SequenceMatch(password string) []match.Match {
|
||||
var matches []match.Match
|
||||
for i := 0; i < len(password); {
|
||||
j := i + 1
|
||||
var seq string
|
||||
var seqName string
|
||||
seqDirection := 0
|
||||
for seqCandidateName, seqCandidate := range SEQUENCES {
|
||||
iN := strings.Index(seqCandidate, string(password[i]))
|
||||
var jN int
|
||||
if j < len(password) {
|
||||
jN = strings.Index(seqCandidate, string(password[j]))
|
||||
} else {
|
||||
jN = -1
|
||||
}
|
||||
|
||||
if iN > -1 && jN > -1 {
|
||||
direction := jN - iN
|
||||
if direction == 1 || direction == -1 {
|
||||
seq = seqCandidate
|
||||
seqName = seqCandidateName
|
||||
seqDirection = direction
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if seq != "" {
|
||||
for ;; {
|
||||
var prevN, curN int
|
||||
if j < len(password) {
|
||||
prevChar, curChar := password[j - 1], password[j]
|
||||
prevN, curN = strings.Index(seq, string(prevChar)), strings.Index(seq, string(curChar))
|
||||
}
|
||||
|
||||
if j == len(password) || curN - prevN != seqDirection {
|
||||
if j - i > 2 {
|
||||
matches = append(matches, match.Match{Pattern:"sequence",
|
||||
I:i,
|
||||
J:j-1,
|
||||
Token:password[i:j],
|
||||
DictionaryName:seqName,
|
||||
DictionaryLength: len(seq),
|
||||
Ascending:(seqDirection == 1)})
|
||||
}
|
||||
break
|
||||
} else {
|
||||
j += 1
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
i = j
|
||||
}
|
||||
return matches
|
||||
}
|
@ -8,6 +8,7 @@ import (
|
||||
"regexp"
|
||||
"github.com/nbutton23/zxcvbn-go/utils/math"
|
||||
"github.com/nbutton23/zxcvbn-go/matching"
|
||||
"github.com/nbutton23/zxcvbn-go/adjacency"
|
||||
)
|
||||
|
||||
|
||||
@ -29,7 +30,7 @@ const (
|
||||
type MinEntropyMatch struct {
|
||||
Password string
|
||||
Entropy float64
|
||||
MatchSequence []match.Match //TODO ?
|
||||
MatchSequence []match.Match
|
||||
CrackTime float64
|
||||
CrackTimeDisplay string
|
||||
Score int
|
||||
@ -37,7 +38,7 @@ type MinEntropyMatch struct {
|
||||
}
|
||||
|
||||
/*
|
||||
Returns minimum entropy TODO
|
||||
Returns minimum entropy
|
||||
|
||||
Takes a list of overlapping matches, returns the non-overlapping sublist with
|
||||
minimum entropy. O(nm) dp alg for length-n password with m candidate matches.
|
||||
@ -164,6 +165,8 @@ func calcEntropy(match match.Match) float64 {
|
||||
entropy = spatialEntropy(match)
|
||||
} else if match.Pattern == "repeat" {
|
||||
entropy = repeatEntropy(match)
|
||||
} else if match.Pattern == "sequence" {
|
||||
entropy = sequenceEntropy(match)
|
||||
}
|
||||
|
||||
match.Entropy = entropy
|
||||
@ -181,8 +184,8 @@ func dictionaryEntropy(match match.Match) float64 {
|
||||
func spatialEntropy(match match.Match) float64 {
|
||||
var s, d float64
|
||||
if match.DictionaryName == "qwerty" || match.DictionaryName == "dvorak" {
|
||||
s = float64(matching.KEYBOARD_STARTING_POSITIONS)
|
||||
d = matching.KEYBOARD_AVG_DEGREE
|
||||
s = float64(len(adjacency.BuildQwerty().Graph))
|
||||
d = adjacency.BuildKeypad().CalculateAvgDegree()
|
||||
} else {
|
||||
s = float64(matching.KEYPAD_STARTING_POSITIONS)
|
||||
d = matching.KEYPAD_AVG_DEGREE
|
||||
@ -190,12 +193,12 @@ func spatialEntropy(match match.Match) float64 {
|
||||
|
||||
possibilities := float64(0)
|
||||
|
||||
lenght := float64(len(match.Token))
|
||||
length := float64(len(match.Token))
|
||||
t := match.Turns
|
||||
|
||||
//TODO: Should this be <= or just < ?
|
||||
//Estimate the number of possible patterns w/ lenght L or less with t turns or less
|
||||
for i := float64(2); i <= lenght + 1; i++ {
|
||||
//Estimate the number of possible patterns w/ length L or less with t turns or less
|
||||
for i := float64(2); i <= length + 1; i++ {
|
||||
possibleTurns := math.Min(float64(t), i - 1)
|
||||
for j := float64(1); j <= possibleTurns + 1; j++ {
|
||||
x := zxcvbn_math.NChoseK(i - 1, j - 1) * s * math.Pow(d, j)
|
||||
@ -204,13 +207,12 @@ func spatialEntropy(match match.Match) float64 {
|
||||
}
|
||||
|
||||
entropy := math.Log2(possibilities)
|
||||
|
||||
//add extra entropu for shifted keys. ( % instead of 5 A instead of a)
|
||||
//Math is similar to extra entropy for uppercase letters in dictionary matches.
|
||||
|
||||
if S := float64(match.ShiftedCount); S > float64(0) {
|
||||
possibilities = float64(0)
|
||||
U := lenght - S
|
||||
U := length - S
|
||||
|
||||
for i := float64(0); i < math.Min(S, U) + 1; i++ {
|
||||
possibilities += zxcvbn_math.NChoseK(S + U, i)
|
||||
@ -221,6 +223,23 @@ func spatialEntropy(match match.Match) float64 {
|
||||
|
||||
return entropy
|
||||
}
|
||||
func sequenceEntropy(match match.Match) float64 {
|
||||
firstChar := match.Token[0]
|
||||
baseEntropy := float64(0)
|
||||
if string(firstChar) == "a" || string(firstChar) == "1" {
|
||||
baseEntropy = float64(0)
|
||||
} else {
|
||||
baseEntropy = math.Log2(float64(match.DictionaryLength))
|
||||
if unicode.IsUpper(rune(firstChar)) {
|
||||
baseEntropy ++
|
||||
}
|
||||
}
|
||||
|
||||
if !match.Ascending {
|
||||
baseEntropy++
|
||||
}
|
||||
return baseEntropy + math.Log2(float64(len(match.Token)))
|
||||
}
|
||||
func extraUpperCaseEntropy(match match.Match) float64 {
|
||||
word := match.Token
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user