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
import (
"log"
"encoding/json"
// "fmt"
"log"
// "fmt"
"github.com/nbutton23/zxcvbn-go/data"
)
type AdjacencyGraph struct {
Graph map[string][]string
Graph map[string][]string
averageDegree float64
Name string
Name string
}
var AdjacencyGph = make(map[string]AdjacencyGraph)
var AdjacencyGph = make(map[string]AdjacencyGraph);
func init() {
log.SetFlags(log.Lshortfile)
AdjacencyGph["qwerty"] = BuildQwerty()
@ -54,7 +54,7 @@ func BuildMacKeypad() AdjacencyGraph {
func GetAdjancencyGraphFromFile(data []byte, name string) AdjacencyGraph {
var graph AdjacencyGraph;
var graph AdjacencyGraph
err := json.Unmarshal(data, &graph)
if err != nil {
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.
//this calculates the average over all keys.
//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) {
return adjGrp.averageDegree
}
@ -83,8 +83,7 @@ func (adjGrp AdjacencyGraph) CalculateAvgDegree() (float64) {
}
adjGrp.averageDegree = avg/count
adjGrp.averageDegree = avg / count
return adjGrp.averageDegree
}

View File

@ -19,11 +19,11 @@ import (
"compress/gzip"
"fmt"
"io"
"strings"
"os"
"time"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
func bindataRead(data []byte, name string) ([]byte, error) {
@ -52,9 +52,9 @@ type asset struct {
}
type bindataFileInfo struct {
name string
size int64
mode os.FileMode
name string
size int64
mode os.FileMode
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
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)}
a := &asset{bytes: bytes, info: info}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -296,7 +296,7 @@ func Asset(name string) ([]byte, error) {
// It simplifies safe initialization of global variables.
func MustAsset(name string) []byte {
a, err := Asset(name)
if (err != nil) {
if err != nil {
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.
var _bindata = map[string]func() (*asset, error){
"data/Dvorak.json": dataDvorakJson,
"data/English.json": dataEnglishJson,
"data/Dvorak.json": dataDvorakJson,
"data/English.json": dataEnglishJson,
"data/FemaleNames.json": dataFemalenamesJson,
"data/Keypad.json": dataKeypadJson,
"data/L33t.json": dataL33tJson,
"data/MacKeypad.json": dataMackeypadJson,
"data/MaleNames.json": dataMalenamesJson,
"data/Passwords.json": dataPasswordsJson,
"data/Qwerty.json": dataQwertyJson,
"data/Surnames.json": dataSurnamesJson,
"data/Keypad.json": dataKeypadJson,
"data/L33t.json": dataL33tJson,
"data/MacKeypad.json": dataMackeypadJson,
"data/MaleNames.json": dataMalenamesJson,
"data/Passwords.json": dataPasswordsJson,
"data/Qwerty.json": dataQwertyJson,
"data/Surnames.json": dataSurnamesJson,
}
// AssetDir returns the file names below a certain
@ -377,78 +377,68 @@ func AssetDir(name string) ([]string, error) {
}
type bintree struct {
Func func() (*asset, error)
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"data": &bintree{nil, map[string]*bintree{
"Dvorak.json": &bintree{dataDvorakJson, map[string]*bintree{
}},
"English.json": &bintree{dataEnglishJson, map[string]*bintree{
}},
"FemaleNames.json": &bintree{dataFemalenamesJson, map[string]*bintree{
}},
"Keypad.json": &bintree{dataKeypadJson, map[string]*bintree{
}},
"L33t.json": &bintree{dataL33tJson, 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{
}},
"Dvorak.json": &bintree{dataDvorakJson, map[string]*bintree{}},
"English.json": &bintree{dataEnglishJson, map[string]*bintree{}},
"FemaleNames.json": &bintree{dataFemalenamesJson, map[string]*bintree{}},
"Keypad.json": &bintree{dataKeypadJson, map[string]*bintree{}},
"L33t.json": &bintree{dataL33tJson, 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
func RestoreAsset(dir, name string) error {
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
data, err := Asset(name)
if err != nil {
return err
}
info, err := AssetInfo(name)
if err != nil {
return err
}
err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
if err != nil {
return err
}
err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
if err != nil {
return err
}
err = os.Chtimes(_filePath(dir, name), info.ModTime(), info.ModTime())
if err != nil {
return err
}
return nil
}
// RestoreAssets restores an asset under the given directory recursively
func RestoreAssets(dir, name string) error {
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
children, err := AssetDir(name)
// File
if err != nil {
return RestoreAsset(dir, name)
}
// Dir
for _, child := range children {
err = RestoreAssets(dir, filepath.Join(name, child))
if err != nil {
return err
}
}
return nil
}
func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

View File

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

View File

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

View File

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

View File

@ -1,32 +1,31 @@
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 (
START_UPPER string = `^[A-Z][^A-Z]+$`
END_UPPER string = `^[^A-Z]+[A-Z]$'`
ALL_UPPER string = `^[A-Z]+$`
END_UPPER string = `^[^A-Z]+[A-Z]$'`
ALL_UPPER string = `^[A-Z]+$`
//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.)
//adjust for your site accordingly if you use another hash function, possibly by
//several orders of magnitude!
SINGLE_GUESS float64 = 0.010
NUM_ATTACKERS float64 = 100 //Cores used to make guesses
//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.)
//adjust for your site accordingly if you use another hash function, possibly by
//several orders of magnitude!
SINGLE_GUESS float64 = 0.010
NUM_ATTACKERS float64 = 100 //Cores used to make guesses
SECONDS_PER_GUESS float64 = SINGLE_GUESS / NUM_ATTACKERS
)
type MinEntropyMatch struct {
Password string
Entropy float64
@ -42,14 +41,14 @@ 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.
*/
*/
func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntropyMatch {
bruteforceCardinality := float64(calcBruteforceCardinality(password))
upToK := make([]float64, len(password))
backPointers := make([]match.Match, len(password))
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 {
if match.J != k {
@ -58,7 +57,7 @@ func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntr
i, j := match.I, match.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)
match.Entropy = calculatedEntropy
candidateEntropy := upTo + calculatedEntropy
@ -81,7 +80,7 @@ func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntr
matchSequence = append(matchSequence, match)
k = match.I - 1
} else {
} else {
k--
}
@ -89,11 +88,11 @@ func MinimumEntropyMatchSequence(password string, matches []match.Match) MinEntr
sort.Sort(match.Matches(matchSequence))
makeBruteForecMatch := func(i, j int) match.Match {
return match.Match{Pattern:"bruteforce",
I:i,
J:j,
Token:password[i:j + 1],
Entropy:math.Log2(math.Pow(bruteforceCardinality, float64(j - i)))}
return match.Match{Pattern: "bruteforce",
I: i,
J: j,
Token: password[i : j+1],
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
for _, match := range matchSequence {
i, j := match.I, match.J
if i - k > 0 {
matchSequenceCopy = append(matchSequenceCopy, makeBruteForecMatch(k, i - 1))
if i-k > 0 {
matchSequenceCopy = append(matchSequenceCopy, makeBruteForecMatch(k, i-1))
}
k = j + 1
matchSequenceCopy = append(matchSequenceCopy, match)
}
if k < len(password) {
matchSequenceCopy = append(matchSequenceCopy, makeBruteForecMatch(k, len(password) - 1))
matchSequenceCopy = append(matchSequenceCopy, makeBruteForecMatch(k, len(password)-1))
}
var minEntropy float64
if len(password) == 0 {
minEntropy = float64(0)
} else {
minEntropy = upToK[len(password) - 1 ]
minEntropy = upToK[len(password)-1]
}
crackTime := roundToXDigits(entropyToCrackTime(minEntropy), 3)
return MinEntropyMatch{Password:password,
Entropy:roundToXDigits(minEntropy, 3),
MatchSequence:matchSequenceCopy,
CrackTime:crackTime,
CrackTimeDisplay:displayTime(crackTime),
Score:crackTimeToScore(crackTime)}
return MinEntropyMatch{Password: password,
Entropy: roundToXDigits(minEntropy, 3),
MatchSequence: matchSequenceCopy,
CrackTime: crackTime,
CrackTimeDisplay: displayTime(crackTime),
Score: crackTimeToScore(crackTime)}
}
func get(a []float64, i int) float64 {
@ -198,10 +197,10 @@ func spatialEntropy(match match.Match) float64 {
//TODO: Should this be <= or just < ?
//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)
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)
possibilities += x
}
}
@ -214,8 +213,8 @@ func spatialEntropy(match match.Match) float64 {
possibilities = float64(0)
U := length - S
for i := float64(0); i < math.Min(S, U) + 1; i++ {
possibilities += zxcvbn_math.NChoseK(S + U, i)
for i := float64(0); i < math.Min(S, U)+1; i++ {
possibilities += zxcvbn_math.NChoseK(S+U, i)
}
entropy += math.Log2(possibilities)
@ -231,7 +230,7 @@ func sequenceEntropy(match match.Match) float64 {
} else {
baseEntropy = math.Log2(float64(match.DictionaryLength))
if unicode.IsUpper(rune(firstChar)) {
baseEntropy ++
baseEntropy++
}
}
@ -321,15 +320,15 @@ func displayTime(seconds float64) string {
if seconds < minute {
return "instant"
} 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 {
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / hour)), "hours")
return fmt.Sprintf(formater, (1 + math.Ceil(seconds/hour)), "hours")
} 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 {
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / month)), "months")
}else if seconds < century {
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / century)), "years")
return fmt.Sprintf(formater, (1 + math.Ceil(seconds/month)), "months")
} else if seconds < century {
return fmt.Sprintf(formater, (1 + math.Ceil(seconds/century)), "years")
} else {
return "centuries"
}

View File

@ -1,11 +1,10 @@
package zxcvbn_math
import "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 float64) float64 {

View File

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