diff --git a/README.md b/README.md index 9d1026b..b09fb4f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # zxcvbn-go zxcvbn password complexity algorithm in golang -This is based off of the zxcvbn algorithm by Dropbox. https://github.com/dropbox/zxcvbn +This is based off of the zxcvbn algorithm by Dropbox. https://github.com/dropbox/python-zxcvbn This is a work in progress diff --git a/adjacency/adjcmartix.go b/adjacency/adjcmartix.go index 6b8da0f..c7dfc15 100644 --- a/adjacency/adjcmartix.go +++ b/adjacency/adjcmartix.go @@ -3,6 +3,7 @@ import ( "log" "encoding/json" "io/ioutil" +// "fmt" ) @@ -58,3 +59,23 @@ func getAdjancencyGraphFromFile(filePath string) AdjacencyGraph { return graph } +//on qwerty, 'g' has degree 6, being adjacent to 'ftyhbv'. '\' has degree 1. +//this calculates the average over all keys. +//TODO double check that i ported this correctly scoring.coffee ln 5 +func (adjGrp AdjacencyGraph) CalculateAvgDegree() (float32) { + var avg float32 + var count float32 + for _, value := range adjGrp.Graph { + + for _, char := range value { + if char != "" || char != " " { + avg += float32(len(char)) + count++ + } + } + + } + + return avg/count +} + diff --git a/matching/matching.go b/matching/matching.go new file mode 100644 index 0000000..1df1f1d --- /dev/null +++ b/matching/matching.go @@ -0,0 +1,150 @@ +package matching +import ( + "strings" + "github.com/bradfitz/slice" + "regexp" + "strconv" +) + +var DICTIONARY_MATCHERS []func(password string) []Match + + const ( + //TODO: Invalid regex for Golang since it has a \2 + DATE_RX_YEAR_SUFFIX string = `((\d{1,2})(\s|-|\/|\\|_|\.)(\d{1,2})(\s|-|\/|\\|_|\.)(19\d{2}|200\d|201\d|\d{2}))` + DATE_RX_YEAR_PREFIX string = `((19\d{2}|200\d|201\d|\d{2})(\s|-|/|\\|_|\.)(\d{1,2})(\s|-|/|\\|_|\.)(\d{1,2}))` + ) +type Match struct { + Pattern string + I, J int + Token string + MatchedWord string + Rank int + DictionaryName string +} + +type DateMatch struct { + Pattern string + I, J int + Token string + Separator string + Day, Month, Year int64 + +} + +func Omnimatch(password string, userInputs []string) []Match { + var rankedUserInputsDir map[string]int + + for i, v := range userInputs { + rankedUserInputsDir[strings.ToLower(v)] = i+1 + } + userInputMatcher := buildDictMatcher("user_inputs", rankedUserInputsDir) + matches := userInputMatcher(password) + + for _, matcher := range DICTIONARY_MATCHERS { + mtemp := matcher(password) + for _,v:= range mtemp { + matches = append(matches, v) + } + } + slice.Sort(matches,func(i, j int)bool{ + //TODO fix this + return false; + }) + return matches +} + + + + +func buildDictMatcher(dictName string, rankedDict map[string]int) func(password string) []Match { + return func (password string) []Match{ + matches := dictionaryMatch(password, rankedDict) + for _, v := range matches { + v.DictionaryName = dictName + } + return matches + } + +} + +func dictionaryMatch(password string, rankedDict map[string]int) []Match{ + length := len(password) + var results []Match + pwLower := strings.ToLower(password) + + for i :=0; i 31 || month > 12 { + return false, 0, 0, 0 + } + + if !(1900 <= year && year <=2019) { + return false, 0, 0, 0 + } + + return true, day, month, year +} + +func DateSepMatch(password string) []DateMatch { + + var matches []DateMatch + + + matcher := regexp.MustCompile(DATE_RX_YEAR_SUFFIX) + for _, v := range matcher.FindAllString(password,len(password)) { + splitV := matcher.FindAllStringSubmatch(v, len(v)) + i := strings.Index(password,v) + j := i+len(v) + day, _ := strconv.ParseInt(splitV[0][4],10,16) + month, _ := strconv.ParseInt(splitV[0][2], 10, 16) + year, _ := strconv.ParseInt(splitV[0][6], 10, 16) + match := DateMatch{Day:day, Month:month, Year:year, Separator:splitV[0][5], I:i, J:j } + matches = append(matches, match) + } + + + matcher = regexp.MustCompile(DATE_RX_YEAR_PREFIX) + for _, v := range matcher.FindAllString(password,len(password)) { + splitV := matcher.FindAllStringSubmatch(v, len(v)) + i := strings.Index(password,v) + j := i+len(v) + day, _ := strconv.ParseInt(splitV[0][4],10,16) + month, _ := strconv.ParseInt(splitV[0][6], 10, 16) + year, _ := strconv.ParseInt(splitV[0][2], 10, 16) + match := DateMatch{Day:day, Month:month, Year:year, Separator:splitV[0][5], I:i, J:j } + matches = append(matches, match) + } + + var out []DateMatch + for _, match := range matches { + if valid, day, month, year := checkDate(match.Day, match.Month, match.Year); valid{ + match.Pattern = "date" + match.Day = day + match.Month = month + match.Year = year + out = append(out, match) + } + } + return out + +} \ No newline at end of file diff --git a/utils/math/mathutils.go b/utils/math/mathutils.go new file mode 100644 index 0000000..5c7c065 --- /dev/null +++ b/utils/math/mathutils.go @@ -0,0 +1,28 @@ +package math + + + +/** +I am surprised that I have to define these. . . Maybe i just didn't look hard enough for a lib. + */ + +//http://blog.plover.com/math/choose.html +func NChoseK(n, k uint) uint64 { + uN := uint64(n) + uK := uint64(k) + if uK > uN { + return 0 + } else if uK == 0 { + return 1 + } + + var r uint64 = 1 + + for d := uint64(1) ; d <= uK; d++ { + r *= uN + r /= d + uN-- + } + + return r +} \ No newline at end of file diff --git a/zxcvbn.go b/zxcvbn.go index 43c1ec5..3c1bfe4 100644 --- a/zxcvbn.go +++ b/zxcvbn.go @@ -1,14 +1,21 @@ package main import ( -// "zxcvbn-go/adjacency" + "zxcvbn-go/adjacency" "fmt" - "zxcvbn-go/frequency" + "zxcvbn-go/utils/math" + "zxcvbn-go/matching" ) -func main(){ +func main() { fmt.Println("Start") -// fmt.Println(adjacency.AdjacencyGph) + fmt.Println(adjacency.AdjacencyGph.Qwerty.CalculateAvgDegree()) + fmt.Println(adjacency.AdjacencyGph.Dvorak.CalculateAvgDegree()) + fmt.Println(adjacency.AdjacencyGph.Keypad.CalculateAvgDegree()) + fmt.Println(adjacency.AdjacencyGph.MacKeypad.CalculateAvgDegree()) - fmt.Println(len(frequency.FreqLists.Passwords)) + + fmt.Println(math.NChoseK(100, 2)) + + fmt.Println(matching.DateSepMatch("1991-09-11jibjab11.9.1991")) } \ No newline at end of file