This commit is contained in:
Nathan Button 2016-01-13 15:49:04 -07:00
parent e3fb346bcb
commit 7fa8f55be0
8 changed files with 220 additions and 232 deletions

View File

@ -1,20 +1,20 @@
package adjacency package adjacency
import ( import (
"log"
"encoding/json" "encoding/json"
// "fmt" "log"
// "fmt"
"github.com/nbutton23/zxcvbn-go/data" "github.com/nbutton23/zxcvbn-go/data"
) )
type AdjacencyGraph struct { type AdjacencyGraph struct {
Graph map[string][]string Graph map[string][]string
averageDegree float64 averageDegree float64
Name string Name string
} }
var AdjacencyGph = make(map[string]AdjacencyGraph)
var AdjacencyGph = make(map[string]AdjacencyGraph);
func init() { func init() {
log.SetFlags(log.Lshortfile) log.SetFlags(log.Lshortfile)
AdjacencyGph["qwerty"] = BuildQwerty() AdjacencyGph["qwerty"] = BuildQwerty()
@ -54,7 +54,7 @@ func BuildMacKeypad() AdjacencyGraph {
func GetAdjancencyGraphFromFile(data []byte, name string) AdjacencyGraph { func GetAdjancencyGraphFromFile(data []byte, name string) AdjacencyGraph {
var graph AdjacencyGraph; var graph AdjacencyGraph
err := json.Unmarshal(data, &graph) err := json.Unmarshal(data, &graph)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@ -66,7 +66,7 @@ func GetAdjancencyGraphFromFile(data []byte, name string) AdjacencyGraph {
//on qwerty, 'g' has degree 6, being adjacent to 'ftyhbv'. '\' has degree 1. //on qwerty, 'g' has degree 6, being adjacent to 'ftyhbv'. '\' has degree 1.
//this calculates the average over all keys. //this calculates the average over all keys.
//TODO double check that i ported this correctly scoring.coffee ln 5 //TODO double check that i ported this correctly scoring.coffee ln 5
func (adjGrp AdjacencyGraph) CalculateAvgDegree() (float64) { func (adjGrp AdjacencyGraph) CalculateAvgDegree() float64 {
if adjGrp.averageDegree != float64(0) { if adjGrp.averageDegree != float64(0) {
return adjGrp.averageDegree return adjGrp.averageDegree
} }
@ -83,8 +83,7 @@ func (adjGrp AdjacencyGraph) CalculateAvgDegree() (float64) {
} }
adjGrp.averageDegree = avg/count adjGrp.averageDegree = avg / count
return adjGrp.averageDegree return adjGrp.averageDegree
} }

View File

@ -19,11 +19,11 @@ import (
"compress/gzip" "compress/gzip"
"fmt" "fmt"
"io" "io"
"strings"
"os"
"time"
"io/ioutil" "io/ioutil"
"os"
"path/filepath" "path/filepath"
"strings"
"time"
) )
func bindataRead(data []byte, name string) ([]byte, error) { func bindataRead(data []byte, name string) ([]byte, error) {
@ -52,9 +52,9 @@ type asset struct {
} }
type bindataFileInfo struct { type bindataFileInfo struct {
name string name string
size int64 size int64
mode os.FileMode mode os.FileMode
modTime time.Time modTime time.Time
} }
@ -93,7 +93,7 @@ func dataDvorakJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/Dvorak.json", size: 8398, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)} info := bindataFileInfo{name: "data/Dvorak.json", size: 8398, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -113,7 +113,7 @@ func dataEnglishJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/English.json", size: 341560, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)} info := bindataFileInfo{name: "data/English.json", size: 341560, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -133,7 +133,7 @@ func dataFemalenamesJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/FemaleNames.json", size: 53909, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)} info := bindataFileInfo{name: "data/FemaleNames.json", size: 53909, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -153,7 +153,7 @@ func dataKeypadJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/Keypad.json", size: 1639, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)} info := bindataFileInfo{name: "data/Keypad.json", size: 1639, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -173,7 +173,7 @@ func dataL33tJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/L33t.json", size: 477, mode: os.FileMode(420), modTime: time.Unix(1444170193, 0)} info := bindataFileInfo{name: "data/L33t.json", size: 477, mode: os.FileMode(420), modTime: time.Unix(1444170193, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -193,7 +193,7 @@ func dataMackeypadJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/MacKeypad.json", size: 1744, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)} info := bindataFileInfo{name: "data/MacKeypad.json", size: 1744, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -213,7 +213,7 @@ func dataMalenamesJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/MaleNames.json", size: 11791, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)} info := bindataFileInfo{name: "data/MaleNames.json", size: 11791, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -233,7 +233,7 @@ func dataPasswordsJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/Passwords.json", size: 67534, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)} info := bindataFileInfo{name: "data/Passwords.json", size: 67534, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -253,7 +253,7 @@ func dataQwertyJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/Qwerty.json", size: 8398, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)} info := bindataFileInfo{name: "data/Qwerty.json", size: 8398, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -273,7 +273,7 @@ func dataSurnamesJson() (*asset, error) {
} }
info := bindataFileInfo{name: "data/Surnames.json", size: 396413, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)} info := bindataFileInfo{name: "data/Surnames.json", size: 396413, mode: os.FileMode(420), modTime: time.Unix(1444169890, 0)}
a := &asset{bytes: bytes, info: info} a := &asset{bytes: bytes, info: info}
return a, nil return a, nil
} }
@ -296,7 +296,7 @@ func Asset(name string) ([]byte, error) {
// It simplifies safe initialization of global variables. // It simplifies safe initialization of global variables.
func MustAsset(name string) []byte { func MustAsset(name string) []byte {
a, err := Asset(name) a, err := Asset(name)
if (err != nil) { if err != nil {
panic("asset: Asset(" + name + "): " + err.Error()) panic("asset: Asset(" + name + "): " + err.Error())
} }
@ -329,16 +329,16 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name. // _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){ var _bindata = map[string]func() (*asset, error){
"data/Dvorak.json": dataDvorakJson, "data/Dvorak.json": dataDvorakJson,
"data/English.json": dataEnglishJson, "data/English.json": dataEnglishJson,
"data/FemaleNames.json": dataFemalenamesJson, "data/FemaleNames.json": dataFemalenamesJson,
"data/Keypad.json": dataKeypadJson, "data/Keypad.json": dataKeypadJson,
"data/L33t.json": dataL33tJson, "data/L33t.json": dataL33tJson,
"data/MacKeypad.json": dataMackeypadJson, "data/MacKeypad.json": dataMackeypadJson,
"data/MaleNames.json": dataMalenamesJson, "data/MaleNames.json": dataMalenamesJson,
"data/Passwords.json": dataPasswordsJson, "data/Passwords.json": dataPasswordsJson,
"data/Qwerty.json": dataQwertyJson, "data/Qwerty.json": dataQwertyJson,
"data/Surnames.json": dataSurnamesJson, "data/Surnames.json": dataSurnamesJson,
} }
// AssetDir returns the file names below a certain // AssetDir returns the file names below a certain
@ -377,78 +377,68 @@ func AssetDir(name string) ([]string, error) {
} }
type bintree struct { type bintree struct {
Func func() (*asset, error) Func func() (*asset, error)
Children map[string]*bintree Children map[string]*bintree
} }
var _bintree = &bintree{nil, map[string]*bintree{ var _bintree = &bintree{nil, map[string]*bintree{
"data": &bintree{nil, map[string]*bintree{ "data": &bintree{nil, map[string]*bintree{
"Dvorak.json": &bintree{dataDvorakJson, map[string]*bintree{ "Dvorak.json": &bintree{dataDvorakJson, map[string]*bintree{}},
}}, "English.json": &bintree{dataEnglishJson, map[string]*bintree{}},
"English.json": &bintree{dataEnglishJson, map[string]*bintree{ "FemaleNames.json": &bintree{dataFemalenamesJson, map[string]*bintree{}},
}}, "Keypad.json": &bintree{dataKeypadJson, map[string]*bintree{}},
"FemaleNames.json": &bintree{dataFemalenamesJson, map[string]*bintree{ "L33t.json": &bintree{dataL33tJson, map[string]*bintree{}},
}}, "MacKeypad.json": &bintree{dataMackeypadJson, map[string]*bintree{}},
"Keypad.json": &bintree{dataKeypadJson, map[string]*bintree{ "MaleNames.json": &bintree{dataMalenamesJson, map[string]*bintree{}},
}}, "Passwords.json": &bintree{dataPasswordsJson, map[string]*bintree{}},
"L33t.json": &bintree{dataL33tJson, map[string]*bintree{ "Qwerty.json": &bintree{dataQwertyJson, map[string]*bintree{}},
}}, "Surnames.json": &bintree{dataSurnamesJson, map[string]*bintree{}},
"MacKeypad.json": &bintree{dataMackeypadJson, map[string]*bintree{
}},
"MaleNames.json": &bintree{dataMalenamesJson, map[string]*bintree{
}},
"Passwords.json": &bintree{dataPasswordsJson, map[string]*bintree{
}},
"Qwerty.json": &bintree{dataQwertyJson, map[string]*bintree{
}},
"Surnames.json": &bintree{dataSurnamesJson, map[string]*bintree{
}},
}}, }},
}} }}
// RestoreAsset restores an asset under the given directory // RestoreAsset restores an asset under the given directory
func RestoreAsset(dir, name string) error { func RestoreAsset(dir, name string) error {
data, err := Asset(name) data, err := Asset(name)
if err != nil { if err != nil {
return err return err
} }
info, err := AssetInfo(name) info, err := AssetInfo(name)
if err != nil { if err != nil {
return err return err
} }
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755)) err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil { if err != nil {
return err return err
} }
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode()) err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil { if err != nil {
return err return err
} }
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime()) err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// RestoreAssets restores an asset under the given directory recursively // RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error { func RestoreAssets(dir, name string) error {
children, err := AssetDir(name) children, err := AssetDir(name)
// File // File
if err != nil { if err != nil {
return RestoreAsset(dir, name) return RestoreAsset(dir, name)
} }
// Dir // Dir
for _, child := range children { for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child)) err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil { if err != nil {
return err return err
} }
} }
return nil return nil
} }
func _filePath(dir, name string) string { func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1) cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...) return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
} }

View File

@ -1,8 +1,9 @@
package frequency package frequency
import ( import (
"log"
"encoding/json" "encoding/json"
"github.com/nbutton23/zxcvbn-go/data" "github.com/nbutton23/zxcvbn-go/data"
"log"
) )
type FrequencyList struct { type FrequencyList struct {
@ -11,6 +12,7 @@ type FrequencyList struct {
} }
var FrequencyLists = make(map[string]FrequencyList) var FrequencyLists = make(map[string]FrequencyList)
func init() { func init() {
maleFilePath := getAsset("data/MaleNames.json") maleFilePath := getAsset("data/MaleNames.json")
femaleFilePath := getAsset("data/FemaleNames.json") femaleFilePath := getAsset("data/FemaleNames.json")
@ -35,7 +37,7 @@ func getAsset(name string) []byte {
} }
func GetStringListFromAsset(data []byte, name string) FrequencyList { func GetStringListFromAsset(data []byte, name string) FrequencyList {
var tempList FrequencyList; var tempList FrequencyList
err := json.Unmarshal(data, &tempList) err := json.Unmarshal(data, &tempList)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)

View File

@ -1,10 +1,11 @@
package match package match
type Matches []Match type Matches []Match
func (s Matches)Len() int {
func (s Matches) Len() int {
return len(s) return len(s)
} }
func (s Matches)Swap(i, j int) { func (s Matches) Swap(i, j int) {
s[i], s[j] = s[j], s[i] s[i], s[j] = s[j], s[i]
} }
func (s Matches) Less(i, j int) bool { func (s Matches) Less(i, j int) bool {
@ -16,19 +17,20 @@ func (s Matches) Less(i, j int) bool {
return false return false
} }
} }
type Match struct { type Match struct {
Pattern string Pattern string
I, J int I, J int
Token string Token string
MatchedWord string MatchedWord string
Rank float64 Rank float64
DictionaryName string DictionaryName string
DictionaryLength int DictionaryLength int
Ascending bool Ascending bool
Turns int Turns int
ShiftedCount int ShiftedCount int
Entropy float64 Entropy float64
RepeatedChar string RepeatedChar string
} }
type DateMatch struct { type DateMatch struct {
@ -37,5 +39,4 @@ type DateMatch struct {
Token string Token string
Separator string Separator string
Day, Month, Year int64 Day, Month, Year int64
} }

View File

@ -1,37 +1,37 @@
package matching package matching
import ( import (
"strings"
"regexp"
"strconv"
"github.com/nbutton23/zxcvbn-go/frequency"
"github.com/nbutton23/zxcvbn-go/adjacency" "github.com/nbutton23/zxcvbn-go/adjacency"
"github.com/nbutton23/zxcvbn-go/frequency"
"github.com/nbutton23/zxcvbn-go/match" "github.com/nbutton23/zxcvbn-go/match"
"regexp"
"sort" "sort"
// "github.com/deckarep/golang-set" "strconv"
"strings"
// "github.com/deckarep/golang-set"
) )
var ( var (
DICTIONARY_MATCHERS []func(password string) []match.Match DICTIONARY_MATCHERS []func(password string) []match.Match
MATCHERS []func(password string) []match.Match MATCHERS []func(password string) []match.Match
ADJACENCY_GRAPHS []adjacency.AdjacencyGraph; ADJACENCY_GRAPHS []adjacency.AdjacencyGraph
KEYBOARD_STARTING_POSITIONS int KEYBOARD_STARTING_POSITIONS int
KEYBOARD_AVG_DEGREE float64 KEYBOARD_AVG_DEGREE float64
KEYPAD_STARTING_POSITIONS int KEYPAD_STARTING_POSITIONS int
KEYPAD_AVG_DEGREE float64 KEYPAD_AVG_DEGREE float64
L33T_TABLE adjacency.AdjacencyGraph L33T_TABLE adjacency.AdjacencyGraph
SEQUENCES map[string]string SEQUENCES map[string]string
) )
const ( const (
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_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}))` DATE_RX_YEAR_PREFIX string = `((19\d{2}|200\d|201\d|\d{2})(\s|-|/|\\|_|\.)(\d{1,2})(\s|-|/|\\|_|\.)(\d{1,2}))`
DATE_WITHOUT_SEP_MATCH string = `\d{4,8}` DATE_WITHOUT_SEP_MATCH string = `\d{4,8}`
) )
func init() { func init() {
loadFrequencyList() loadFrequencyList()
} }
func Omnimatch(password string, userInputs []string) (matches []match.Match) { func Omnimatch(password string, userInputs []string) (matches []match.Match) {
@ -83,10 +83,8 @@ func loadFrequencyList() {
MATCHERS = append(MATCHERS, repeatMatch) MATCHERS = append(MATCHERS, repeatMatch)
MATCHERS = append(MATCHERS, SequenceMatch) MATCHERS = append(MATCHERS, SequenceMatch)
} }
func buildDictMatcher(dictName string, rankedDict map[string]int) func(password string) []match.Match { func buildDictMatcher(dictName string, rankedDict map[string]int) func(password string) []match.Match {
return func(password string) []match.Match { return func(password string) []match.Match {
matches := dictionaryMatch(password, dictName, rankedDict) matches := dictionaryMatch(password, dictName, rankedDict)
@ -105,15 +103,15 @@ func dictionaryMatch(password string, dictionaryName string, rankedDict map[stri
for i := 0; i < length; i++ { for i := 0; i < length; i++ {
for j := i; j < length; j++ { for j := i; j < length; j++ {
word := pwLower[i:j + 1] word := pwLower[i : j+1]
if val, ok := rankedDict[word]; ok { if val, ok := rankedDict[word]; ok {
results = append(results, match.Match{Pattern:"dictionary", results = append(results, match.Match{Pattern: "dictionary",
DictionaryName:dictionaryName, DictionaryName: dictionaryName,
I:i, I: i,
J:j, J: j,
Token:password[i:j + 1], Token: password[i : j+1],
MatchedWord:word, MatchedWord: word,
Rank:float64(val)}) Rank: float64(val)})
} }
} }
} }
@ -160,11 +158,10 @@ func DateSepMatch(password string) []match.DateMatch {
day, _ := strconv.ParseInt(splitV[0][4], 10, 16) day, _ := strconv.ParseInt(splitV[0][4], 10, 16)
month, _ := strconv.ParseInt(splitV[0][2], 10, 16) month, _ := strconv.ParseInt(splitV[0][2], 10, 16)
year, _ := strconv.ParseInt(splitV[0][6], 10, 16) year, _ := strconv.ParseInt(splitV[0][6], 10, 16)
match := match.DateMatch{Day:day, Month:month, Year:year, Separator:splitV[0][5], I:i, J:j } match := match.DateMatch{Day: day, Month: month, Year: year, Separator: splitV[0][5], I: i, J: j}
matches = append(matches, match) matches = append(matches, match)
} }
matcher = regexp.MustCompile(DATE_RX_YEAR_PREFIX) matcher = regexp.MustCompile(DATE_RX_YEAR_PREFIX)
for _, v := range matcher.FindAllString(password, len(password)) { for _, v := range matcher.FindAllString(password, len(password)) {
splitV := matcher.FindAllStringSubmatch(v, len(v)) splitV := matcher.FindAllStringSubmatch(v, len(v))
@ -173,7 +170,7 @@ func DateSepMatch(password string) []match.DateMatch {
day, _ := strconv.ParseInt(splitV[0][4], 10, 16) day, _ := strconv.ParseInt(splitV[0][4], 10, 16)
month, _ := strconv.ParseInt(splitV[0][6], 10, 16) month, _ := strconv.ParseInt(splitV[0][6], 10, 16)
year, _ := strconv.ParseInt(splitV[0][2], 10, 16) year, _ := strconv.ParseInt(splitV[0][2], 10, 16)
match := match.DateMatch{Day:day, Month:month, Year:year, Separator:splitV[0][5], I:i, J:j } match := match.DateMatch{Day: day, Month: month, Year: year, Separator: splitV[0][5], I: i, J: j}
matches = append(matches, match) matches = append(matches, match)
} }
@ -190,11 +187,13 @@ func DateSepMatch(password string) []match.DateMatch {
return out return out
} }
type DateMatchCandidate struct { type DateMatchCandidate struct {
DayMonth string DayMonth string
Year string Year string
I, J int I, J int
} }
//TODO I think Im doing this wrong. //TODO I think Im doing this wrong.
func dateWithoutSepMatch(password string) (matches []match.DateMatch) { func dateWithoutSepMatch(password string) (matches []match.DateMatch) {
matcher := regexp.MustCompile(DATE_WITHOUT_SEP_MATCH) matcher := regexp.MustCompile(DATE_WITHOUT_SEP_MATCH)
@ -210,14 +209,14 @@ func dateWithoutSepMatch(password string) (matches []match.DateMatch) {
candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[2:], v[0:2], i, j)) candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[2:], v[0:2], i, j))
//2-digityear suffix //2-digityear suffix
candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[0:lastIndex - 2], v[lastIndex - 2:], i, j)) candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[0:lastIndex-2], v[lastIndex-2:], i, j))
} }
if length >= 6 { if length >= 6 {
//4-digit year prefix //4-digit year prefix
candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[4:], v[0:4], i, j)) candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[4:], v[0:4], i, j))
//4-digit year sufix //4-digit year sufix
candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[0:lastIndex - 4], v[lastIndex - 4:], i, j)) candidatesRoundOne = append(candidatesRoundOne, buildDateMatchCandidate(v[0:lastIndex-4], v[lastIndex-4:], i, j))
} }
var candidatesRoundTwo []match.DateMatch var candidatesRoundTwo []match.DateMatch
@ -232,7 +231,7 @@ func dateWithoutSepMatch(password string) (matches []match.DateMatch) {
} }
func buildDateMatchCandidate(dayMonth, year string, i, j int) DateMatchCandidate { func buildDateMatchCandidate(dayMonth, year string, i, j int) DateMatchCandidate {
return DateMatchCandidate{DayMonth: dayMonth, Year:year, I:i, J:j} return DateMatchCandidate{DayMonth: dayMonth, Year: year, I: i, J: j}
} }
func buildDateMatchCandidateTwo(day, month byte, year string, i, j int) match.DateMatch { func buildDateMatchCandidateTwo(day, month byte, year string, i, j int) match.DateMatch {
@ -242,10 +241,9 @@ func buildDateMatchCandidateTwo(day, month byte, year string, i, j int) match.Da
intMonth, _ := strconv.ParseInt(sMonth, 10, 16) intMonth, _ := strconv.ParseInt(sMonth, 10, 16)
intYear, _ := strconv.ParseInt(year, 10, 16) intYear, _ := strconv.ParseInt(year, 10, 16)
return match.DateMatch{Day:intDay, Month:intMonth, Year:intYear, I:i, J:j} return match.DateMatch{Day: intDay, Month: intMonth, Year: intYear, I: i, J: j}
} }
func spatialMatch(password string) (matches []match.Match) { func spatialMatch(password string) (matches []match.Match) {
for _, graph := range ADJACENCY_GRAPHS { for _, graph := range ADJACENCY_GRAPHS {
matches = append(matches, spatialMatchHelper(password, graph)...) matches = append(matches, spatialMatchHelper(password, graph)...)
@ -254,14 +252,14 @@ func spatialMatch(password string) (matches []match.Match) {
} }
func spatialMatchHelper(password string, graph adjacency.AdjacencyGraph) (matches []match.Match) { func spatialMatchHelper(password string, graph adjacency.AdjacencyGraph) (matches []match.Match) {
for i := 0; i < len(password) - 1; { for i := 0; i < len(password)-1; {
j := i + 1 j := i + 1
lastDirection := -99 //and int that it should never be! lastDirection := -99 //and int that it should never be!
turns := 0 turns := 0
shiftedCount := 0 shiftedCount := 0
for ;; { for {
prevChar := password[j - 1] prevChar := password[j-1]
found := false found := false
foundDirection := -1 foundDirection := -1
curDirection := -1 curDirection := -1
@ -300,8 +298,8 @@ func spatialMatchHelper(password string, graph adjacency.AdjacencyGraph) (matche
} else { } else {
// otherwise push the pattern discovered so far, if any... // otherwise push the pattern discovered so far, if any...
// don't consider length 1 or 2 chains. // don't consider length 1 or 2 chains.
if j - i > 2 { if j-i > 2 {
matches = append(matches, match.Match{Pattern:"spatial", I:i, J:j - 1, Token:password[i:j], DictionaryName:graph.Name, Turns:turns, ShiftedCount:shiftedCount }) matches = append(matches, match.Match{Pattern: "spatial", I: i, J: j - 1, Token: password[i:j], DictionaryName: graph.Name, Turns: turns, ShiftedCount: shiftedCount})
} }
// . . . and then start a new search from the rest of the password // . . . and then start a new search from the rest of the password
i = j i = j
@ -364,11 +362,11 @@ func repeatMatch(password string) []match.Match {
iPos := i - currentStreak iPos := i - currentStreak
jPos := i - 1 jPos := i - 1
matches = append(matches, match.Match{ matches = append(matches, match.Match{
Pattern:"repeat", Pattern: "repeat",
I:iPos, I: iPos,
J:jPos, J: jPos,
Token:password[iPos:jPos + 1], Token: password[iPos : jPos+1],
RepeatedChar:prev}) RepeatedChar: prev})
currentStreak = 1 currentStreak = 1
} else { } else {
currentStreak = 1 currentStreak = 1
@ -381,11 +379,11 @@ func repeatMatch(password string) []match.Match {
iPos := i - currentStreak + 1 iPos := i - currentStreak + 1
jPos := i jPos := i
matches = append(matches, match.Match{ matches = append(matches, match.Match{
Pattern:"repeat", Pattern: "repeat",
I:iPos, I: iPos,
J:jPos, J: jPos,
Token:password[iPos:jPos + 1], Token: password[iPos : jPos+1],
RepeatedChar:prev}) RepeatedChar: prev})
} }
return matches return matches
} }
@ -419,22 +417,22 @@ func SequenceMatch(password string) []match.Match {
} }
if seq != "" { if seq != "" {
for ;; { for {
var prevN, curN int var prevN, curN int
if j < len(password) { if j < len(password) {
prevChar, curChar := password[j - 1], password[j] prevChar, curChar := password[j-1], password[j]
prevN, curN = strings.Index(seq, string(prevChar)), strings.Index(seq, string(curChar)) prevN, curN = strings.Index(seq, string(prevChar)), strings.Index(seq, string(curChar))
} }
if j == len(password) || curN - prevN != seqDirection { if j == len(password) || curN-prevN != seqDirection {
if j - i > 2 { if j-i > 2 {
matches = append(matches, match.Match{Pattern:"sequence", matches = append(matches, match.Match{Pattern: "sequence",
I:i, I: i,
J:j-1, J: j - 1,
Token:password[i:j], Token: password[i:j],
DictionaryName:seqName, DictionaryName: seqName,
DictionaryLength: len(seq), DictionaryLength: len(seq),
Ascending:(seqDirection == 1)}) Ascending: (seqDirection == 1)})
} }
break break
} else { } else {

View File

@ -1,32 +1,31 @@
package scoring package scoring
import (
"github.com/nbutton23/zxcvbn-go/match"
"unicode"
"fmt"
"math"
"sort"
"regexp"
"github.com/nbutton23/zxcvbn-go/utils/math"
"github.com/nbutton23/zxcvbn-go/matching"
"github.com/nbutton23/zxcvbn-go/adjacency"
)
import (
"fmt"
"github.com/nbutton23/zxcvbn-go/adjacency"
"github.com/nbutton23/zxcvbn-go/match"
"github.com/nbutton23/zxcvbn-go/matching"
"github.com/nbutton23/zxcvbn-go/utils/math"
"math"
"regexp"
"sort"
"unicode"
)
const ( const (
START_UPPER string = `^[A-Z][^A-Z]+$` START_UPPER string = `^[A-Z][^A-Z]+$`
END_UPPER string = `^[^A-Z]+[A-Z]$'` END_UPPER string = `^[^A-Z]+[A-Z]$'`
ALL_UPPER string = `^[A-Z]+$` ALL_UPPER string = `^[A-Z]+$`
//for a hash function like bcrypt/scrypt/PBKDF2, 10ms per guess is a safe lower bound. //for a hash function like bcrypt/scrypt/PBKDF2, 10ms per guess is a safe lower bound.
//(usually a guess would take longer -- this assumes fast hardware and a small work factor.) //(usually a guess would take longer -- this assumes fast hardware and a small work factor.)
//adjust for your site accordingly if you use another hash function, possibly by //adjust for your site accordingly if you use another hash function, possibly by
//several orders of magnitude! //several orders of magnitude!
SINGLE_GUESS float64 = 0.010 SINGLE_GUESS float64 = 0.010
NUM_ATTACKERS float64 = 100 //Cores used to make guesses NUM_ATTACKERS float64 = 100 //Cores used to make guesses
SECONDS_PER_GUESS float64 = SINGLE_GUESS / NUM_ATTACKERS SECONDS_PER_GUESS float64 = SINGLE_GUESS / NUM_ATTACKERS
) )
type MinEntropyMatch struct { type MinEntropyMatch struct {
Password string Password string
Entropy float64 Entropy float64
@ -42,14 +41,14 @@ Returns minimum entropy
Takes a list of overlapping matches, returns the non-overlapping sublist with 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. minimum entropy. O(nm) dp alg for length-n password with m candidate matches.
*/ */
func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntropyMatch { func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntropyMatch {
bruteforceCardinality := float64(calcBruteforceCardinality(password)) bruteforceCardinality := float64(calcBruteforceCardinality(password))
upToK := make([]float64, len(password)) upToK := make([]float64, len(password))
backPointers := make([]match.Match, len(password)) backPointers := make([]match.Match, len(password))
for k := 0; k < len(password); k++ { for k := 0; k < len(password); k++ {
upToK[k] = get(upToK, k - 1) + math.Log2(bruteforceCardinality) upToK[k] = get(upToK, k-1) + math.Log2(bruteforceCardinality)
for _, match := range matches { for _, match := range matches {
if match.J != k { if match.J != k {
@ -58,7 +57,7 @@ func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntr
i, j := match.I, match.J i, j := match.I, match.J
// see if best entropy up to i-1 + entropy of match is less that current min at j // see if best entropy up to i-1 + entropy of match is less that current min at j
upTo := get(upToK, i - 1) upTo := get(upToK, i-1)
calculatedEntropy := calcEntropy(match) calculatedEntropy := calcEntropy(match)
match.Entropy = calculatedEntropy match.Entropy = calculatedEntropy
candidateEntropy := upTo + calculatedEntropy candidateEntropy := upTo + calculatedEntropy
@ -81,7 +80,7 @@ func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntr
matchSequence = append(matchSequence, match) matchSequence = append(matchSequence, match)
k = match.I - 1 k = match.I - 1
} else { } else {
k-- k--
} }
@ -89,11 +88,11 @@ func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntr
sort.Sort(match.Matches(matchSequence)) sort.Sort(match.Matches(matchSequence))
makeBruteForecMatch := func(i, j int) match.Match { makeBruteForecMatch := func(i, j int) match.Match {
return match.Match{Pattern:"bruteforce", return match.Match{Pattern: "bruteforce",
I:i, I: i,
J:j, J: j,
Token:password[i:j + 1], Token: password[i : j+1],
Entropy:math.Log2(math.Pow(bruteforceCardinality, float64(j - i)))} Entropy: math.Log2(math.Pow(bruteforceCardinality, float64(j-i)))}
} }
@ -101,30 +100,30 @@ func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntr
var matchSequenceCopy []match.Match var matchSequenceCopy []match.Match
for _, match := range matchSequence { for _, match := range matchSequence {
i, j := match.I, match.J i, j := match.I, match.J
if i - k > 0 { if i-k > 0 {
matchSequenceCopy = append(matchSequenceCopy, makeBruteForecMatch(k, i - 1)) matchSequenceCopy = append(matchSequenceCopy, makeBruteForecMatch(k, i-1))
} }
k = j + 1 k = j + 1
matchSequenceCopy = append(matchSequenceCopy, match) matchSequenceCopy = append(matchSequenceCopy, match)
} }
if k < len(password) { if k < len(password) {
matchSequenceCopy = append(matchSequenceCopy, makeBruteForecMatch(k, len(password) - 1)) matchSequenceCopy = append(matchSequenceCopy, makeBruteForecMatch(k, len(password)-1))
} }
var minEntropy float64 var minEntropy float64
if len(password) == 0 { if len(password) == 0 {
minEntropy = float64(0) minEntropy = float64(0)
} else { } else {
minEntropy = upToK[len(password) - 1 ] minEntropy = upToK[len(password)-1]
} }
crackTime := roundToXDigits(entropyToCrackTime(minEntropy), 3) crackTime := roundToXDigits(entropyToCrackTime(minEntropy), 3)
return MinEntropyMatch{Password:password, return MinEntropyMatch{Password: password,
Entropy:roundToXDigits(minEntropy, 3), Entropy: roundToXDigits(minEntropy, 3),
MatchSequence:matchSequenceCopy, MatchSequence: matchSequenceCopy,
CrackTime:crackTime, CrackTime: crackTime,
CrackTimeDisplay:displayTime(crackTime), CrackTimeDisplay: displayTime(crackTime),
Score:crackTimeToScore(crackTime)} Score: crackTimeToScore(crackTime)}
} }
func get(a []float64, i int) float64 { func get(a []float64, i int) float64 {
@ -198,10 +197,10 @@ func spatialEntropy(match match.Match) float64 {
//TODO: Should this be <= or just < ? //TODO: Should this be <= or just < ?
//Estimate the number of possible patterns w/ length L or less with t turns or less //Estimate the number of possible patterns w/ length L or less with t turns or less
for i := float64(2); i <= length + 1; i++ { for i := float64(2); i <= length+1; i++ {
possibleTurns := math.Min(float64(t), i - 1) possibleTurns := math.Min(float64(t), i-1)
for j := float64(1); j <= possibleTurns + 1; j++ { for j := float64(1); j <= possibleTurns+1; j++ {
x := zxcvbn_math.NChoseK(i - 1, j - 1) * s * math.Pow(d, j) x := zxcvbn_math.NChoseK(i-1, j-1) * s * math.Pow(d, j)
possibilities += x possibilities += x
} }
} }
@ -214,8 +213,8 @@ func spatialEntropy(match match.Match) float64 {
possibilities = float64(0) possibilities = float64(0)
U := length - S U := length - S
for i := float64(0); i < math.Min(S, U) + 1; i++ { for i := float64(0); i < math.Min(S, U)+1; i++ {
possibilities += zxcvbn_math.NChoseK(S + U, i) possibilities += zxcvbn_math.NChoseK(S+U, i)
} }
entropy += math.Log2(possibilities) entropy += math.Log2(possibilities)
@ -231,7 +230,7 @@ func sequenceEntropy(match match.Match) float64 {
} else { } else {
baseEntropy = math.Log2(float64(match.DictionaryLength)) baseEntropy = math.Log2(float64(match.DictionaryLength))
if unicode.IsUpper(rune(firstChar)) { if unicode.IsUpper(rune(firstChar)) {
baseEntropy ++ baseEntropy++
} }
} }
@ -321,15 +320,15 @@ func displayTime(seconds float64) string {
if seconds < minute { if seconds < minute {
return "instant" return "instant"
} else if seconds < hour { } else if seconds < hour {
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / minute)), "minutes") return fmt.Sprintf(formater, (1 + math.Ceil(seconds/minute)), "minutes")
} else if seconds < day { } else if seconds < day {
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / hour)), "hours") return fmt.Sprintf(formater, (1 + math.Ceil(seconds/hour)), "hours")
} else if seconds < month { } else if seconds < month {
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / day)), "days") return fmt.Sprintf(formater, (1 + math.Ceil(seconds/day)), "days")
} else if seconds < year { } else if seconds < year {
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / month)), "months") return fmt.Sprintf(formater, (1 + math.Ceil(seconds/month)), "months")
}else if seconds < century { } else if seconds < century {
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / century)), "years") return fmt.Sprintf(formater, (1 + math.Ceil(seconds/century)), "years")
} else { } else {
return "centuries" return "centuries"
} }

View File

@ -1,11 +1,10 @@
package zxcvbn_math package zxcvbn_math
import "math" import "math"
/** /**
I am surprised that I have to define these. . . Maybe i just didn't look hard enough for a lib. 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 //http://blog.plover.com/math/choose.html
func NChoseK(n, k float64) float64 { func NChoseK(n, k float64) float64 {

View File

@ -3,8 +3,8 @@ package zxcvbn
import ( import (
"github.com/nbutton23/zxcvbn-go/matching" "github.com/nbutton23/zxcvbn-go/matching"
"github.com/nbutton23/zxcvbn-go/scoring" "github.com/nbutton23/zxcvbn-go/scoring"
"time"
"github.com/nbutton23/zxcvbn-go/utils/math" "github.com/nbutton23/zxcvbn-go/utils/math"
"time"
) )
//func main() { //func main() {