Merge pull request #12 from nbutton23/button-l33t

Button l33t
This commit is contained in:
Nathan Button 2016-01-29 14:56:06 -07:00
commit 5fb7f48ad1
6 changed files with 163 additions and 42 deletions

View File

@ -56,7 +56,7 @@ Bug reports and pull requests welcome!
Project Status Project Status
------------------------------------------------------------------------ ------------------------------------------------------------------------
Use zxcvbn_test.go to check how close to feature parity the project is. Use zxcvbn_test.go to check how close to feature parity the project is.
------------------------------------------------------------------------ ------------------------------------------------------------------------
Acknowledgment Acknowledgment

View File

@ -21,6 +21,7 @@ func init() {
AdjacencyGph["dvorak"] = BuildDvorak() AdjacencyGph["dvorak"] = BuildDvorak()
AdjacencyGph["keypad"] = BuildKeypad() AdjacencyGph["keypad"] = BuildKeypad()
AdjacencyGph["macKeypad"] = BuildMacKeypad() AdjacencyGph["macKeypad"] = BuildMacKeypad()
AdjacencyGph["l33t"] = BuildLeet()
} }
func BuildQwerty() AdjacencyGraph { func BuildQwerty() AdjacencyGraph {
@ -51,6 +52,13 @@ func BuildMacKeypad() AdjacencyGraph {
} }
return GetAdjancencyGraphFromFile(data, "mac_keypad") return GetAdjancencyGraphFromFile(data, "mac_keypad")
} }
func BuildLeet() AdjacencyGraph {
data, err := zxcvbn_data.Asset("data/L33t.json")
if err != nil {
panic("Can't find asset")
}
return GetAdjancencyGraphFromFile(data, "keypad")
}
func GetAdjancencyGraphFromFile(data []byte, name string) AdjacencyGraph { func GetAdjancencyGraphFromFile(data []byte, name string) AdjacencyGraph {

View File

@ -156,7 +156,7 @@ func SequenceEntropy(match match.Match, dictionaryLength int, ascending bool) fl
baseEntropy = float64(0) baseEntropy = float64(0)
} else { } else {
baseEntropy = math.Log2(float64(dictionaryLength)) baseEntropy = math.Log2(float64(dictionaryLength))
//TODO: should this be just the first or any char //TODO: should this be just the first or any char?
if unicode.IsUpper(rune(firstChar)) { if unicode.IsUpper(rune(firstChar)) {
baseEntropy++ baseEntropy++
} }
@ -167,3 +167,28 @@ func SequenceEntropy(match match.Match, dictionaryLength int, ascending bool) fl
} }
return baseEntropy + math.Log2(float64(len(match.Token))) return baseEntropy + math.Log2(float64(len(match.Token)))
} }
func ExtraLeetEntropy(match match.Match, password string) float64 {
var subsitutions float64
var unsub float64
subPassword := password[match.I:match.J]
for index, char := range subPassword {
if string(char) != string(match.Token[index]) {
subsitutions++
} else {
//TODO: Make this only true for 1337 chars that are not subs?
unsub++
}
}
var possibilities float64
for i := float64(0); i <= math.Min(subsitutions, unsub)+1; i++ {
possibilities += zxcvbn_math.NChoseK(subsitutions+unsub, i)
}
if possibilities <= 1 {
return float64(1)
}
return math.Log2(possibilities)
}

View File

@ -8,7 +8,6 @@ import (
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
// "github.com/deckarep/golang-set"
"github.com/nbutton23/zxcvbn-go/entropy" "github.com/nbutton23/zxcvbn-go/entropy"
) )
@ -65,6 +64,7 @@ func loadFrequencyList() {
KEYBOARD_STARTING_POSITIONS = len(adjacency.AdjacencyGph["querty"].Graph) KEYBOARD_STARTING_POSITIONS = len(adjacency.AdjacencyGph["querty"].Graph)
KEYPAD_AVG_DEGREE = adjacency.AdjacencyGph["keypad"].CalculateAvgDegree() KEYPAD_AVG_DEGREE = adjacency.AdjacencyGph["keypad"].CalculateAvgDegree()
KEYPAD_STARTING_POSITIONS = len(adjacency.AdjacencyGph["keypad"].Graph) KEYPAD_STARTING_POSITIONS = len(adjacency.AdjacencyGph["keypad"].Graph)
L33T_TABLE = adjacency.AdjacencyGph["l33t"]
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["qwerty"]) ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["qwerty"])
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["dvorak"]) ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["dvorak"])
@ -83,6 +83,7 @@ func loadFrequencyList() {
MATCHERS = append(MATCHERS, SpatialMatch) MATCHERS = append(MATCHERS, SpatialMatch)
MATCHERS = append(MATCHERS, RepeatMatch) MATCHERS = append(MATCHERS, RepeatMatch)
MATCHERS = append(MATCHERS, SequenceMatch) MATCHERS = append(MATCHERS, SequenceMatch)
MATCHERS = append(MATCHERS, l33tMatch)
} }
@ -319,34 +320,67 @@ func spatialMatchHelper(password string, graph adjacency.AdjacencyGraph) (matche
return matches return matches
} }
func relevantL33tSubtable(password string) adjacency.AdjacencyGraph {
var releventSubs adjacency.AdjacencyGraph func l33tMatch(password string) []match.Match {
for _, char := range password {
if len(L33T_TABLE.Graph[string(char)]) > 0 { subsitutions := relevantL33tSubtable(password)
releventSubs.Graph[string(char)] = L33T_TABLE.Graph[string(char)]
permutations := getAllPermutationsOfLeetSubstitutions(password, subsitutions)
var matches []match.Match
for _, permutation := range permutations {
for _, mather := range DICTIONARY_MATCHERS {
matches = append(matches,mather(permutation)...)
} }
} }
return releventSubs for _, match := range matches {
match.Entropy += entropy.ExtraLeetEntropy(match, password)
match.DictionaryName = match.DictionaryName + "_3117"
}
return matches
} }
//TODO yeah this is a little harder than i expect. . . func getAllPermutationsOfLeetSubstitutions(password string, substitutionsMap map[string][]string) []string {
//func enumerateL33tSubs(table adjacency.AdjacencyGraph) []string {
// var subs [][]string var permutations []string
//
// dedup := func(subs []string) []string { for index, char := range password {
// deduped := mapset.NewSetFromSlice(subs) for value, splice := range substitutionsMap {
// return deduped.ToSlice() for _, sub := range splice {
// } if string(char) == sub {
// var permutation string
// for i,v := range table.Graph { permutation = password[:index]+value+password[index+1:]
// var nextSubs []string
// for _, subChar := range v { permutations = append(permutations, permutation)
// if index < len(permutation) {
// } tempPermutations := getAllPermutationsOfLeetSubstitutions(permutation[index + 1:], substitutionsMap)
// for _, temp := range tempPermutations {
// } permutations = append(permutations, permutation[:index + 1] + temp)
//} }
}
}
}
}
}
return permutations
}
func relevantL33tSubtable(password string) map[string][]string {
relevantSubs := make(map[string][]string)
for key, values := range L33T_TABLE.Graph {
for _, value := range values {
if strings.Contains(password, value) {
relevantSubs[key] = append(relevantSubs[key], value)
}
}
}
return relevantSubs
}
func RepeatMatch(password string) []match.Match { func RepeatMatch(password string) []match.Match {
var matches []match.Match var matches []match.Match

View File

@ -5,6 +5,7 @@ import (
"github.com/nbutton23/zxcvbn-go/match" "github.com/nbutton23/zxcvbn-go/match"
"strings" "strings"
"testing" "testing"
"fmt"
) )
//DateSepMatch("1991-09-11jibjab11.9.1991") //DateSepMatch("1991-09-11jibjab11.9.1991")
@ -120,3 +121,58 @@ func TestDateWithoutSepMatch(t *testing.T) {
matches := dateWithoutSepMatch("11091991") matches := dateWithoutSepMatch("11091991")
assert.Len(t, matches, 1, "Lenght should be 1") assert.Len(t, matches, 1, "Lenght should be 1")
} }
//l33t
func TestLeetSubTable(t *testing.T){
subs := relevantL33tSubtable("password")
assert.Len(t, subs, 0, "password should produce no leet subs")
subs = relevantL33tSubtable("p4ssw0rd")
assert.Len(t, subs, 2, "p4ssw0rd should produce 2 subs")
subs = relevantL33tSubtable("1eet")
assert.Len(t, subs, 2, "1eet should produce 2 subs")
assert.Equal(t, subs["i"][0], "1")
assert.Equal(t, subs["l"][0], "1")
subs = relevantL33tSubtable("4pple@pple")
assert.Len(t, subs, 1, "4pple@pple should produce 1 subs")
assert.Len(t, subs["a"], 2)
}
func TestPermutationsOfLeetSubstitutions(t *testing.T){
password := "p4ssw0rd" //[passw0rd, password, p4ssword]
possibleSubs := relevantL33tSubtable(password)
permutations := getAllPermutationsOfLeetSubstitutions(password, possibleSubs)
assert.Len(t, permutations, 3, "There should be 3 permutations for "+password)
password = "p4$sw0rd" //[pa$sw0rd, passw0rd, password, pa$sword, p4ssw0rd, p4ssword, p4$sword]
possibleSubs = relevantL33tSubtable(password)
permutations = getAllPermutationsOfLeetSubstitutions(password, possibleSubs)
assert.Len(t, permutations, 7, "There should be 7 (? check my math) permutations for "+password)
password = "p4$$w0rd" //[pa$sw0rd, passw0rd, password, pa$sword, p4ssw0rd, p4ssword, p4$sword]
possibleSubs = relevantL33tSubtable(password)
permutations = getAllPermutationsOfLeetSubstitutions(password, possibleSubs)
assert.Len(t, permutations, 15, "Check my math 2*2*2*2 - 1 "+password)
password = "1337"
possibleSubs = relevantL33tSubtable(password)
permutations = getAllPermutationsOfLeetSubstitutions(password, possibleSubs)
assert.Len(t, permutations, 35, "check my math 3*2*2*3 -1 ")
}
func TestLeet(t *testing.T){
password := "p4ssw0rd"
matches := l33tMatch(password)
fmt.Println(matches[0].J)
}

View File

@ -3,26 +3,24 @@ package zxcvbn
import ( import (
"testing" "testing"
"fmt"
"math" "math"
"strconv" "strconv"
"fmt"
) )
/** /**
Use these test to see how close to feature parity the library is. Use these test to see how close to feature parity the library is.
*/ */
const ( const (
allowableError = float64(0.01) allowableError = float64(0.05)
) )
type failedTest struct { type failedTest struct {
Password string Password string
Expect float64 Expect float64
Actual float64 Actual float64
PError float64 PError float64
} }
var failedTests []failedTest var failedTests []failedTest
@ -32,11 +30,11 @@ func TestPasswordStrength(t *testing.T) {
//Expected calculated by running zxcvbn-python //Expected calculated by running zxcvbn-python
runTest(t, "zxcvbn", float64(6.845490050944376)) runTest(t, "zxcvbn", float64(6.845490050944376))
runTest(t, "Tr0ub4dour&3",float64(17.296) ) runTest(t, "Tr0ub4dour&3", float64(17.296))
runTest(t,"qwER43@!", float64(26.44) ) runTest(t, "qwER43@!", float64(26.44))
runTest(t,"correcthorsebatterystaple", float64(45.212) ) runTest(t, "correcthorsebatterystaple", float64(45.212))
runTest(t,"coRrecth0rseba++ery9.23.2007staple$", float64(66.018) ) runTest(t, "coRrecth0rseba++ery9.23.2007staple$", float64(66.018))
runTest(t,"D0g..................", float64(20.678) ) runTest(t, "D0g..................", float64(20.678))
runTest(t, "abcdefghijk987654321", float64(11.951)) runTest(t, "abcdefghijk987654321", float64(11.951))
runTest(t, "neverforget13/3/1997", float64(32.628)) runTest(t, "neverforget13/3/1997", float64(32.628))
runTest(t, "1qaz2wsx3edc", float64(19.314)) runTest(t, "1qaz2wsx3edc", float64(19.314))
@ -71,20 +69,20 @@ func TestPasswordStrength(t *testing.T) {
fmt.Printf(formatString, test.Password, allowableError, test.PError, test.Expect, test.Actual) fmt.Printf(formatString, test.Password, allowableError, test.PError, test.Expect, test.Actual)
} }
pTestPassed := (float64(numTestRan - len(failedTests))/ float64(numTestRan))* float64(100) pTestPassed := (float64(numTestRan-len(failedTests)) / float64(numTestRan)) * float64(100)
fmt.Println("\n % of the test passed " + strconv.FormatFloat(pTestPassed, 'f', -1, 64) ) fmt.Println("\n % of the test passed " + strconv.FormatFloat(pTestPassed, 'f', -1, 64))
} }
func runTest(t *testing.T, password string, pythonEntropy float64) { func runTest(t *testing.T, password string, pythonEntropy float64) {
//Calculated by running it through python-zxcvbn //Calculated by running it through python-zxcvbn
goEntropy := GoPasswordStrength(password, nil) goEntropy := GoPasswordStrength(password, nil)
perror := math.Abs(goEntropy-pythonEntropy)/pythonEntropy perror := math.Abs(goEntropy-pythonEntropy) / pythonEntropy
numTestRan++ numTestRan++
if perror > allowableError { if perror > allowableError {
failedTests = append(failedTests, failedTest{Password:password, Expect:pythonEntropy, Actual:goEntropy,PError:perror}) failedTests = append(failedTests, failedTest{Password: password, Expect: pythonEntropy, Actual: goEntropy, PError: perror})
} }
} }