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) {
@ -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())
} }
@ -380,28 +380,19 @@ 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{
}},
}}, }},
}} }}
@ -451,4 +442,3 @@ 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,6 +17,7 @@ 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
@ -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,19 +1,20 @@
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
@ -29,9 +30,8 @@ const (
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
@ -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() {