2016-03-08 16:57:50 +00:00
|
|
|
package matching
|
|
|
|
|
|
|
|
import (
|
2017-11-02 13:14:31 +00:00
|
|
|
"strings"
|
|
|
|
|
2016-06-19 13:33:56 +00:00
|
|
|
"github.com/nbutton23/zxcvbn-go/adjacency"
|
2016-03-08 16:57:50 +00:00
|
|
|
"github.com/nbutton23/zxcvbn-go/entropy"
|
2016-06-19 13:33:56 +00:00
|
|
|
"github.com/nbutton23/zxcvbn-go/match"
|
2016-03-08 16:57:50 +00:00
|
|
|
)
|
|
|
|
|
2018-08-29 02:30:34 +00:00
|
|
|
const spatialMatcherName = "SPATIAL"
|
2017-11-02 13:14:31 +00:00
|
|
|
|
2018-08-29 02:30:34 +00:00
|
|
|
//FilterSpatialMatcher can be pass to zxcvbn-go.PasswordStrength to skip that matcher
|
2017-11-02 13:14:31 +00:00
|
|
|
func FilterSpatialMatcher(m match.Matcher) bool {
|
2018-08-29 02:30:34 +00:00
|
|
|
return m.ID == spatialMatcherName
|
2017-11-02 13:14:31 +00:00
|
|
|
}
|
|
|
|
|
2016-03-08 16:57:50 +00:00
|
|
|
func spatialMatch(password string) (matches []match.Match) {
|
2018-08-29 02:30:34 +00:00
|
|
|
for _, graph := range adjacencyGraphs {
|
2016-03-08 16:57:50 +00:00
|
|
|
if graph.Graph != nil {
|
|
|
|
matches = append(matches, spatialMatchHelper(password, graph)...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return matches
|
|
|
|
}
|
|
|
|
|
2018-08-29 02:30:34 +00:00
|
|
|
func spatialMatchHelper(password string, graph adjacency.Graph) (matches []match.Match) {
|
2016-03-08 16:57:50 +00:00
|
|
|
|
|
|
|
for i := 0; i < len(password)-1; {
|
|
|
|
j := i + 1
|
|
|
|
lastDirection := -99 //an int that it should never be!
|
|
|
|
turns := 0
|
|
|
|
shiftedCount := 0
|
|
|
|
|
|
|
|
for {
|
|
|
|
prevChar := password[j-1]
|
|
|
|
found := false
|
|
|
|
foundDirection := -1
|
|
|
|
curDirection := -1
|
|
|
|
//My graphs seem to be wrong. . . and where the hell is qwerty
|
|
|
|
adjacents := graph.Graph[string(prevChar)]
|
|
|
|
//Consider growing pattern by one character if j hasn't gone over the edge
|
|
|
|
if j < len(password) {
|
|
|
|
curChar := password[j]
|
|
|
|
for _, adj := range adjacents {
|
2018-08-29 02:30:34 +00:00
|
|
|
curDirection++
|
2016-03-08 16:57:50 +00:00
|
|
|
|
|
|
|
if strings.Index(adj, string(curChar)) != -1 {
|
|
|
|
found = true
|
|
|
|
foundDirection = curDirection
|
|
|
|
|
|
|
|
if strings.Index(adj, string(curChar)) == 1 {
|
|
|
|
//index 1 in the adjacency means the key is shifted, 0 means unshifted: A vs a, % vs 5, etc.
|
|
|
|
//for example, 'q' is adjacent to the entry '2@'. @ is shifted w/ index 1, 2 is unshifted.
|
2018-08-29 02:30:34 +00:00
|
|
|
shiftedCount++
|
2016-03-08 16:57:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if lastDirection != foundDirection {
|
|
|
|
//adding a turn is correct even in the initial case when last_direction is null:
|
|
|
|
//every spatial pattern starts with a turn.
|
2018-08-29 02:30:34 +00:00
|
|
|
turns++
|
2016-03-08 16:57:50 +00:00
|
|
|
lastDirection = foundDirection
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//if the current pattern continued, extend j and try to grow again
|
|
|
|
if found {
|
2018-08-29 02:30:34 +00:00
|
|
|
j++
|
2016-03-08 16:57:50 +00:00
|
|
|
} else {
|
|
|
|
//otherwise push the pattern discovered so far, if any...
|
|
|
|
//don't consider length 1 or 2 chains.
|
|
|
|
if j-i > 2 {
|
|
|
|
matchSpc := match.Match{Pattern: "spatial", I: i, J: j - 1, Token: password[i:j], DictionaryName: graph.Name}
|
|
|
|
matchSpc.Entropy = entropy.SpatialEntropy(matchSpc, turns, shiftedCount)
|
|
|
|
matches = append(matches, matchSpc)
|
|
|
|
}
|
|
|
|
//. . . and then start a new search from the rest of the password
|
|
|
|
i = j
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
return matches
|
|
|
|
}
|