Fixed spatial Entropy scoring. Added sequence scoring.

This commit is contained in:
Nathan Button 2015-10-14 09:38:47 -06:00
parent 80ad1b1d96
commit a9a908cd1d
4 changed files with 114 additions and 27 deletions

View File

@ -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")

View File

@ -23,6 +23,8 @@ type Match struct {
MatchedWord string
Rank float64
DictionaryName string
DictionaryLength int
Ascending bool
Turns int
ShiftedCount int
Entropy float64

View File

@ -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) {
@ -54,7 +56,7 @@ func Omnimatch(password string, userInputs []string) (matches []match.Match) {
func loadFrequencyList() {
for n, list := range frequency.FrequencyLists {
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS,buildDictMatcher(n, buildRankedDict(list.List)))
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher(n, buildRankedDict(list.List)))
}
KEYBOARD_AVG_DEGREE = adjacency.AdjacencyGph["querty"].CalculateAvgDegree()
@ -66,13 +68,20 @@ func loadFrequencyList() {
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["dvorak"])
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["keypad"])
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["macKeypad"])
//
// l33tFilePath, _ := filepath.Abs("adjacency/L33t.json")
// L33T_TABLE = adjacency.GetAdjancencyGraphFromFile(l33tFilePath, "l33t")
//
// 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)
}
@ -351,13 +360,13 @@ func repeatMatch(password string) []match.Match {
currentStreak++
} else if currentStreak > 2 {
iPos := i-currentStreak
jPos := i-1
iPos := i - currentStreak
jPos := i - 1
matches = append(matches, match.Match{
Pattern:"repeat",
I:iPos,
J:jPos,
Token:password[iPos:jPos+1],
Token:password[iPos:jPos + 1],
RepeatedChar:prev})
currentStreak = 1
} else {
@ -368,7 +377,7 @@ func repeatMatch(password string) []match.Match {
}
if currentStreak > 2 {
iPos := i - currentStreak+1
iPos := i - currentStreak + 1
jPos := i
matches = append(matches, match.Match{
Pattern:"repeat",
@ -378,4 +387,62 @@ func repeatMatch(password string) []match.Match {
RepeatedChar:prev})
}
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
}

View File

@ -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