Scoring partly done. Its now in a state that it can be used. =D
This commit is contained in:
parent
bcad144b2d
commit
e479b1bf0a
|
@ -0,0 +1 @@
|
||||||
|
zxcvbn
|
|
@ -9,25 +9,25 @@ import (
|
||||||
nbutton: Really the value is not as important to me than they don't change, which happened during development.
|
nbutton: Really the value is not as important to me than they don't change, which happened during development.
|
||||||
*/
|
*/
|
||||||
func TestCalculateDegreeQwert(t *testing.T) {
|
func TestCalculateDegreeQwert(t *testing.T) {
|
||||||
avgDegreeQwert := AdjacencyGph.Qwerty.CalculateAvgDegree()
|
avgDegreeQwert := buildQwerty().CalculateAvgDegree()
|
||||||
|
|
||||||
assert.Equal(t, float32(1.531915), avgDegreeQwert, "Avg degree for qwerty should be 1.531915")
|
assert.Equal(t, float32(1.531915), avgDegreeQwert, "Avg degree for qwerty should be 1.531915")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCalculateDegreeDvorak(t *testing.T) {
|
func TestCalculateDegreeDvorak(t *testing.T) {
|
||||||
avgDegreeQwert := AdjacencyGph.Dvorak.CalculateAvgDegree()
|
avgDegreeQwert := buildDvorak().CalculateAvgDegree()
|
||||||
|
|
||||||
assert.Equal(t, float32(1.531915), avgDegreeQwert, "Avg degree for dvorak should be 1.531915")
|
assert.Equal(t, float32(1.531915), avgDegreeQwert, "Avg degree for dvorak should be 1.531915")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCalculateDegreeKeypad(t *testing.T) {
|
func TestCalculateDegreeKeypad(t *testing.T) {
|
||||||
avgDegreeQwert := AdjacencyGph.Keypad.CalculateAvgDegree()
|
avgDegreeQwert := buildKeypad().CalculateAvgDegree()
|
||||||
|
|
||||||
assert.Equal(t, float32(0.62222224), avgDegreeQwert, "Avg degree for keypad should be 0.62222224")
|
assert.Equal(t, float32(0.62222224), avgDegreeQwert, "Avg degree for keypad should be 0.62222224")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCalculateDegreeMacKepad(t *testing.T) {
|
func TestCalculateDegreeMacKepad(t *testing.T) {
|
||||||
avgDegreeQwert := AdjacencyGph.MacKeypad.CalculateAvgDegree()
|
avgDegreeQwert := buildMacKeypad().CalculateAvgDegree()
|
||||||
|
|
||||||
assert.Equal(t, float32(0.6458333), avgDegreeQwert, "Avg degree for mackeyPad should be 0.6458333")
|
assert.Equal(t, float32(0.6458333), avgDegreeQwert, "Avg degree for mackeyPad should be 0.6458333")
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,42 +10,31 @@ import (
|
||||||
|
|
||||||
type AdjacencyGraph struct {
|
type AdjacencyGraph struct {
|
||||||
Graph map[string][6]string
|
Graph map[string][6]string
|
||||||
averageDegree float32
|
averageDegree float64
|
||||||
Name string
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var AdjacencyGph []AdjacencyGraph;
|
|
||||||
func init(){
|
|
||||||
//todo get currentloc so that i don't have to know the whole path
|
|
||||||
log.SetFlags(log.Lshortfile)
|
|
||||||
AdjacencyGph = append(AdjacencyGph, buildQwerty())
|
|
||||||
AdjacencyGph = append(AdjacencyGph, buildDvorak())
|
|
||||||
AdjacencyGph = append(AdjacencyGph, buildKeypad())
|
|
||||||
AdjacencyGph = append(AdjacencyGph, buildMacKeypad())
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func buildQwerty() AdjacencyGraph {
|
func buildQwerty() AdjacencyGraph {
|
||||||
filePath, _ := filepath.Abs("adjacency/Qwerty.json")
|
filePath, _ := filepath.Abs("Qwerty.json")
|
||||||
return getAdjancencyGraphFromFile(filePath, "qwerty")
|
return GetAdjancencyGraphFromFile(filePath, "qwerty")
|
||||||
}
|
}
|
||||||
func buildDvorak() AdjacencyGraph {
|
func buildDvorak() AdjacencyGraph {
|
||||||
filePath, _ := filepath.Abs("adjacency/Dvorak.json")
|
filePath, _ := filepath.Abs("Dvorak.json")
|
||||||
return getAdjancencyGraphFromFile(filePath, "dvorak")
|
return GetAdjancencyGraphFromFile(filePath, "dvorak")
|
||||||
}
|
}
|
||||||
func buildKeypad() AdjacencyGraph {
|
func buildKeypad() AdjacencyGraph {
|
||||||
filePath, _ := filepath.Abs("adjacency/Keypad.json")
|
filePath, _ := filepath.Abs("Keypad.json")
|
||||||
return getAdjancencyGraphFromFile(filePath, "keypad")
|
return GetAdjancencyGraphFromFile(filePath, "keypad")
|
||||||
}
|
}
|
||||||
func buildMacKeypad() AdjacencyGraph {
|
func buildMacKeypad() AdjacencyGraph {
|
||||||
filePath, _ := filepath.Abs("adjacency/MacKeypad.json")
|
filePath, _ := filepath.Abs("MacKeypad.json")
|
||||||
return getAdjancencyGraphFromFile(filePath, "mac_keypad")
|
return GetAdjancencyGraphFromFile(filePath, "mac_keypad")
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAdjancencyGraphFromFile(filePath string, name string) AdjacencyGraph {
|
func GetAdjancencyGraphFromFile(filePath string, name string) AdjacencyGraph {
|
||||||
data, err := ioutil.ReadFile(filePath)
|
data, err := ioutil.ReadFile(filePath)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -65,17 +54,17 @@ func getAdjancencyGraphFromFile(filePath string, 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() (float32) {
|
func (adjGrp AdjacencyGraph) CalculateAvgDegree() (float64) {
|
||||||
if adjGrp.averageDegree != float32(0) {
|
if adjGrp.averageDegree != float64(0) {
|
||||||
return adjGrp.averageDegree
|
return adjGrp.averageDegree
|
||||||
}
|
}
|
||||||
var avg float32
|
var avg float64
|
||||||
var count float32
|
var count float64
|
||||||
for _, value := range adjGrp.Graph {
|
for _, value := range adjGrp.Graph {
|
||||||
|
|
||||||
for _, char := range value {
|
for _, char := range value {
|
||||||
if char != "" || char != " " {
|
if char != "" || char != " " {
|
||||||
avg += float32(len(char))
|
avg += float64(len(char))
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
package match
|
||||||
|
|
||||||
|
type Matches []Match
|
||||||
|
func (s Matches)Len() int {
|
||||||
|
return len(s)
|
||||||
|
}
|
||||||
|
func (s Matches)Swap(i, j int) {
|
||||||
|
s[i], s[j] = s[j], s[i]
|
||||||
|
}
|
||||||
|
func (s Matches) Less(i, j int) bool {
|
||||||
|
if s[i].I < s[j].I {
|
||||||
|
return true
|
||||||
|
} else if s[i].I == s[j].I {
|
||||||
|
return s[i].J < s[j].J
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
type Match struct {
|
||||||
|
Pattern string
|
||||||
|
I, J int
|
||||||
|
Token string
|
||||||
|
MatchedWord string
|
||||||
|
Rank float64
|
||||||
|
DictionaryName string
|
||||||
|
Turns int
|
||||||
|
ShiftedCount int
|
||||||
|
Entropy float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type DateMatch struct {
|
||||||
|
Pattern string
|
||||||
|
I, J int
|
||||||
|
Token string
|
||||||
|
Separator string
|
||||||
|
Day, Month, Year int64
|
||||||
|
|
||||||
|
}
|
|
@ -1,85 +1,95 @@
|
||||||
package matching
|
package matching
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
"github.com/bradfitz/slice"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"zxcvbn-go/frequency"
|
"zxcvbn-go/frequency"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"zxcvbn-go/adjacency"
|
"zxcvbn-go/adjacency"
|
||||||
|
"zxcvbn-go/match"
|
||||||
|
"sort"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
DICTIONARY_MATCHERS []func(password string) []Match
|
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
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
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_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}`
|
)
|
||||||
)
|
|
||||||
type Match struct {
|
|
||||||
Pattern string
|
|
||||||
I, J int
|
|
||||||
Token string
|
|
||||||
MatchedWord string
|
|
||||||
Rank int
|
|
||||||
DictionaryName string
|
|
||||||
Turns int
|
|
||||||
ShiftedCount int
|
|
||||||
}
|
|
||||||
|
|
||||||
type DateMatch struct {
|
|
||||||
Pattern string
|
|
||||||
I, J int
|
|
||||||
Token string
|
|
||||||
Separator string
|
|
||||||
Day, Month, Year int64
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
loadFrequencyList()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Omnimatch(password string, userInputs []string) []Match {
|
func Omnimatch(password string, userInputs []string) (matches []match.Match) {
|
||||||
|
|
||||||
userInputMatcher := buildDictMatcher("user_inputs", buildRankedDict(userInputs))
|
if DICTIONARY_MATCHERS == nil || ADJACENCY_GRAPHS == nil {
|
||||||
matches := userInputMatcher(password)
|
loadFrequencyList()
|
||||||
|
|
||||||
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
|
if userInputs != nil {
|
||||||
return false;
|
userInputMatcher := buildDictMatcher("user_inputs", buildRankedDict(userInputs))
|
||||||
})
|
matches = userInputMatcher(password)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, matcher := range MATCHERS {
|
||||||
|
mtemp := matcher(password)
|
||||||
|
matches = append(matches, mtemp...)
|
||||||
|
}
|
||||||
|
sort.Sort(match.Matches(matches))
|
||||||
return matches
|
return matches
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadFrequencyList(){
|
func loadFrequencyList() {
|
||||||
maleFilePath, _ := filepath.Abs("frequency/MaleNames.json")
|
maleFilePath, _ := filepath.Abs("frequency/MaleNames.json")
|
||||||
femaleFilePath, _ := filepath.Abs("frequency/FemaleNames.json")
|
femaleFilePath, _ := filepath.Abs("frequency/FemaleNames.json")
|
||||||
surnameFilePath, _ := filepath.Abs("frequency/Surnames.json")
|
surnameFilePath, _ := filepath.Abs("frequency/Surnames.json")
|
||||||
englishFilePath, _ := filepath.Abs("frequency/English.json")
|
englishFilePath, _ := filepath.Abs("frequency/English.json")
|
||||||
passwordsFilePath, _ := filepath.Abs("frequency/Passwords.json")
|
passwordsFilePath, _ := filepath.Abs("frequency/Passwords.json")
|
||||||
|
|
||||||
|
|
||||||
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("MaleNames", buildRankedDict(frequency.GetStringListFromFile(maleFilePath))))
|
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("MaleNames", buildRankedDict(frequency.GetStringListFromFile(maleFilePath))))
|
||||||
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("FemaleNames", buildRankedDict(frequency.GetStringListFromFile(femaleFilePath))))
|
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("FemaleNames", buildRankedDict(frequency.GetStringListFromFile(femaleFilePath))))
|
||||||
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("Surnames", buildRankedDict(frequency.GetStringListFromFile(surnameFilePath))))
|
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("Surnames", buildRankedDict(frequency.GetStringListFromFile(surnameFilePath))))
|
||||||
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("English", buildRankedDict(frequency.GetStringListFromFile(englishFilePath))))
|
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("English", buildRankedDict(frequency.GetStringListFromFile(englishFilePath))))
|
||||||
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("Passwords", buildRankedDict(frequency.GetStringListFromFile(passwordsFilePath))))
|
DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, buildDictMatcher("Passwords", buildRankedDict(frequency.GetStringListFromFile(passwordsFilePath))))
|
||||||
|
|
||||||
|
qwertyfilePath, _ := filepath.Abs("adjacency/Qwerty.json")
|
||||||
|
dvorakfilePath, _ := filepath.Abs("adjacency/Dvorak.json")
|
||||||
|
keypadfilePath, _ := filepath.Abs("adjacency/Keypad.json")
|
||||||
|
macKeypadfilePath, _ := filepath.Abs("adjacency/MacKeypad.json")
|
||||||
|
|
||||||
|
qwertGraph := adjacency.GetAdjancencyGraphFromFile(qwertyfilePath, "qwert")
|
||||||
|
keypadGraph := adjacency.GetAdjancencyGraphFromFile(keypadfilePath, "keypad")
|
||||||
|
|
||||||
|
|
||||||
|
KEYBOARD_AVG_DEGREE = qwertGraph.CalculateAvgDegree()
|
||||||
|
KEYBOARD_STARTING_POSITIONS = len(qwertGraph.Graph)
|
||||||
|
KEYPAD_AVG_DEGREE = keypadGraph.CalculateAvgDegree()
|
||||||
|
KEYPAD_STARTING_POSITIONS = len(keypadGraph.Graph)
|
||||||
|
|
||||||
|
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, qwertGraph)
|
||||||
|
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.GetAdjancencyGraphFromFile(dvorakfilePath, "dvorak"))
|
||||||
|
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, keypadGraph)
|
||||||
|
ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.GetAdjancencyGraphFromFile(macKeypadfilePath, "macKepad"))
|
||||||
|
|
||||||
|
MATCHERS = append(MATCHERS, DICTIONARY_MATCHERS...)
|
||||||
|
MATCHERS = append(MATCHERS, SpatialMatch)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
func buildDictMatcher(dictName string, rankedDict map[string]int) func(password string) []Match {
|
func buildDictMatcher(dictName string, rankedDict map[string]int) func(password string) []match.Match {
|
||||||
return func (password string) []Match{
|
return func(password string) []match.Match {
|
||||||
matches := dictionaryMatch(password, rankedDict)
|
matches := dictionaryMatch(password, dictName, rankedDict)
|
||||||
for _, v := range matches {
|
for _, v := range matches {
|
||||||
v.DictionaryName = dictName
|
v.DictionaryName = dictName
|
||||||
}
|
}
|
||||||
|
@ -88,21 +98,22 @@ func buildDictMatcher(dictName string, rankedDict map[string]int) func(password
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func dictionaryMatch(password string, rankedDict map[string]int) []Match{
|
func dictionaryMatch(password string, dictionaryName string, rankedDict map[string]int) []match.Match {
|
||||||
length := len(password)
|
length := len(password)
|
||||||
var results []Match
|
var results []match.Match
|
||||||
pwLower := strings.ToLower(password)
|
pwLower := strings.ToLower(password)
|
||||||
|
|
||||||
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{Pattern:"dictionary",
|
results = append(results, match.Match{Pattern:"dictionary",
|
||||||
I:i,
|
DictionaryName:dictionaryName,
|
||||||
J:j,
|
I:i,
|
||||||
Token:password[i:j+1],
|
J:j,
|
||||||
MatchedWord:word,
|
Token:password[i:j + 1],
|
||||||
Rank:val})
|
MatchedWord:word,
|
||||||
|
Rank:float64(val)})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,16 +123,16 @@ func dictionaryMatch(password string, rankedDict map[string]int) []Match{
|
||||||
|
|
||||||
func buildRankedDict(unrankedList []string) map[string]int {
|
func buildRankedDict(unrankedList []string) map[string]int {
|
||||||
|
|
||||||
result :=make(map[string]int)
|
result := make(map[string]int)
|
||||||
|
|
||||||
for i, v := range unrankedList {
|
for i, v := range unrankedList {
|
||||||
result[strings.ToLower(v)] = i+1
|
result[strings.ToLower(v)] = i + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkDate(day, month, year int64)( bool, int64, int64, int64){
|
func checkDate(day, month, year int64) (bool, int64, int64, int64) {
|
||||||
if (12 <= month && month <= 31) && day <= 12 {
|
if (12 <= month && month <= 31) && day <= 12 {
|
||||||
day, month = month, day
|
day, month = month, day
|
||||||
}
|
}
|
||||||
|
@ -130,46 +141,45 @@ func checkDate(day, month, year int64)( bool, int64, int64, int64){
|
||||||
return false, 0, 0, 0
|
return false, 0, 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
if !(1900 <= year && year <=2019) {
|
if !(1900 <= year && year <= 2019) {
|
||||||
return false, 0, 0, 0
|
return false, 0, 0, 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, day, month, year
|
return true, day, month, year
|
||||||
}
|
}
|
||||||
|
|
||||||
func DateSepMatch(password string) []DateMatch {
|
func DateSepMatch(password string) []match.DateMatch {
|
||||||
|
|
||||||
var matches []DateMatch
|
|
||||||
|
|
||||||
|
var matches []match.DateMatch
|
||||||
|
|
||||||
matcher := regexp.MustCompile(DATE_RX_YEAR_SUFFIX)
|
matcher := regexp.MustCompile(DATE_RX_YEAR_SUFFIX)
|
||||||
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))
|
||||||
i := strings.Index(password,v)
|
i := strings.Index(password, v)
|
||||||
j := i+len(v)
|
j := i + len(v)
|
||||||
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 := 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))
|
||||||
i := strings.Index(password,v)
|
i := strings.Index(password, v)
|
||||||
j := i+len(v)
|
j := i + len(v)
|
||||||
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 := 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
var out []DateMatch
|
var out []match.DateMatch
|
||||||
for _, match := range matches {
|
for _, match := range matches {
|
||||||
if valid, day, month, year := checkDate(match.Day, match.Month, match.Year); valid{
|
if valid, day, month, year := checkDate(match.Day, match.Month, match.Year); valid {
|
||||||
match.Pattern = "date"
|
match.Pattern = "date"
|
||||||
match.Day = day
|
match.Day = day
|
||||||
match.Month = month
|
match.Month = month
|
||||||
|
@ -180,39 +190,37 @@ func DateSepMatch(password string) []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) []DateMatch {
|
func dateWithoutSepMatch(password string) (matches []match.DateMatch) {
|
||||||
var matches []DateMatch
|
|
||||||
|
|
||||||
matcher := regexp.MustCompile(DATE_WITHOUT_SEP_MATCH)
|
matcher := regexp.MustCompile(DATE_WITHOUT_SEP_MATCH)
|
||||||
for _, v := range matcher.FindAllString(password,len(password)) {
|
for _, v := range matcher.FindAllString(password, len(password)) {
|
||||||
i := strings.Index(password, v)
|
i := strings.Index(password, v)
|
||||||
j := i + len(v)
|
j := i + len(v)
|
||||||
length := len(v)
|
length := len(v)
|
||||||
lastIndex := length-1
|
lastIndex := length - 1
|
||||||
var candidatesRoundOne []DateMatchCandidate
|
var candidatesRoundOne []DateMatchCandidate
|
||||||
|
|
||||||
if length <= 6 {
|
if length <= 6 {
|
||||||
//2-digit year prefix
|
//2-digit year prefix
|
||||||
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 []DateMatch
|
var candidatesRoundTwo []match.DateMatch
|
||||||
for _, c := range candidatesRoundOne {
|
for _, c := range candidatesRoundOne {
|
||||||
if len(c.DayMonth) == 2 {
|
if len(c.DayMonth) == 2 {
|
||||||
candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0], c.DayMonth[1], c.Year, c.I, c.J))
|
candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0], c.DayMonth[1], c.Year, c.I, c.J))
|
||||||
|
@ -227,84 +235,81 @@ 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) DateMatch {
|
func buildDateMatchCandidateTwo(day, month byte, year string, i, j int) match.DateMatch {
|
||||||
sDay := string(day)
|
sDay := string(day)
|
||||||
sMonth := string(month)
|
sMonth := string(month)
|
||||||
intDay, _ := strconv.ParseInt(sDay, 10, 16)
|
intDay, _ := strconv.ParseInt(sDay, 10, 16)
|
||||||
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 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) []Match{
|
|
||||||
var matches []Match
|
|
||||||
|
|
||||||
for _,graph := range adjacency.AdjacencyGph {
|
func SpatialMatch(password string) (matches []match.Match) {
|
||||||
|
for _, graph := range ADJACENCY_GRAPHS {
|
||||||
matches = append(matches, spatialMatchHelper(password, graph)...)
|
matches = append(matches, spatialMatchHelper(password, graph)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
return matches
|
return matches
|
||||||
}
|
}
|
||||||
|
|
||||||
func spatialMatchHelper(password string, graph adjacency.AdjacencyGraph) []Match{
|
func spatialMatchHelper(password string, graph adjacency.AdjacencyGraph) (matches []match.Match) {
|
||||||
var matches []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
|
||||||
adjacents := graph.Graph[string(prevChar)]
|
adjacents := graph.Graph[string(prevChar)]
|
||||||
// Consider growing pattern by one character if j hasn't gone over the edge
|
// Consider growing pattern by one character if j hasn't gone over the edge
|
||||||
if j < len(password) {
|
if j < len(password) {
|
||||||
curChar := password[j]
|
curChar := password[j]
|
||||||
for _,adj := range adjacents {
|
for _, adj := range adjacents {
|
||||||
curDirection += 1
|
curDirection += 1
|
||||||
|
|
||||||
if strings.Index(adj, string(curChar)) != -1 {
|
if strings.Index(adj, string(curChar)) != -1 {
|
||||||
found = true
|
found = true
|
||||||
foundDirection = curDirection
|
foundDirection = curDirection
|
||||||
|
|
||||||
if strings.Index(adj, string(curChar)) == 1 {
|
if strings.Index(adj, string(curChar)) == 1 {
|
||||||
// index 1 in the adjacency means the key is shifted, 0 means unshifted: A vs a, % vs 5, etc.
|
// index 1 in the adjacency means the key is shifted, 0 means unshifted: A vs a, % vs 5, etc.
|
||||||
// for example, 'q' is adjacent to the entry '2@'. @ is shifted w/ index 1, 2 is unshifted.
|
// for example, 'q' is adjacent to the entry '2@'. @ is shifted w/ index 1, 2 is unshifted.
|
||||||
|
|
||||||
shiftedCount += 1
|
shiftedCount += 1
|
||||||
}
|
|
||||||
|
|
||||||
if lastDirection != foundDirection {
|
|
||||||
// adding a turn is correct even in the initial case when last_direction is null:
|
|
||||||
// every spatial pattern starts with a turn.
|
|
||||||
turns += 1
|
|
||||||
lastDirection = foundDirection
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the current pattern continued, extend j and try to grow again
|
if lastDirection != foundDirection {
|
||||||
if found {
|
// adding a turn is correct even in the initial case when last_direction is null:
|
||||||
j +=1
|
// every spatial pattern starts with a turn.
|
||||||
} else {
|
turns += 1
|
||||||
// otherwise push the pattern discovered so far, if any...
|
lastDirection = foundDirection
|
||||||
|
}
|
||||||
// don't consider length 1 or 2 chains.
|
break
|
||||||
if j - i > 2 {
|
|
||||||
matches = append(matches, Match{Pattern:"spatial", I:i, J:j, 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
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the current pattern continued, extend j and try to grow again
|
||||||
|
if found {
|
||||||
|
j += 1
|
||||||
|
} 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 })
|
||||||
|
}
|
||||||
|
// . . . and then start a new search from the rest of the password
|
||||||
|
i = j
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
return matches
|
return matches
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,315 @@
|
||||||
|
package scoring
|
||||||
|
import (
|
||||||
|
"zxcvbn-go/match"
|
||||||
|
"unicode"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"sort"
|
||||||
|
"regexp"
|
||||||
|
"zxcvbn-go/utils/math"
|
||||||
|
"zxcvbn-go/matching"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
const (
|
||||||
|
START_UPPER string = `^[A-Z][^A-Z]+$`
|
||||||
|
END_UPPER string = `^[^A-Z]+[A-Z]$'`
|
||||||
|
ALL_UPPER string = `^[A-Z]+$`
|
||||||
|
SINGLE_GUESS float64 = 0.010
|
||||||
|
NUM_ATTACKERS float64 = 100
|
||||||
|
SECONDS_PER_GUESS float64 = SINGLE_GUESS / NUM_ATTACKERS
|
||||||
|
)
|
||||||
|
type MinEntropyMatch struct {
|
||||||
|
Password string
|
||||||
|
Entropy float64
|
||||||
|
MatchSequence []match.Match //TODO ?
|
||||||
|
CrackTime float64
|
||||||
|
CrackTimeDisplay string
|
||||||
|
Score int
|
||||||
|
CalcTime float64
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Returns minimum entropy TODO
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
for _, match := range matches {
|
||||||
|
if match.J != k {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
calculatedEntropy := calcEntropy(match)
|
||||||
|
match.Entropy = calculatedEntropy
|
||||||
|
candidateEntropy := upTo + calculatedEntropy
|
||||||
|
|
||||||
|
if candidateEntropy < upToK[j] {
|
||||||
|
upToK[j] = candidateEntropy
|
||||||
|
match.Entropy = candidateEntropy
|
||||||
|
backPointers[j] = match
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// walk backwards and decode the best sequence
|
||||||
|
var matchSequence []match.Match
|
||||||
|
passwordLen := len(password)
|
||||||
|
passwordLen--
|
||||||
|
for k := passwordLen; k >= 0; {
|
||||||
|
match := backPointers[k]
|
||||||
|
if match.Pattern != "" {
|
||||||
|
matchSequence = append(matchSequence, match)
|
||||||
|
k = match.I - 1
|
||||||
|
|
||||||
|
} else {
|
||||||
|
k--
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
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)))}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
k := 0
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
k = j + 1
|
||||||
|
matchSequenceCopy = append(matchSequenceCopy, match)
|
||||||
|
}
|
||||||
|
|
||||||
|
if k < len(password) {
|
||||||
|
matchSequenceCopy = append(matchSequenceCopy, makeBruteForecMatch(k, len(password) - 1))
|
||||||
|
}
|
||||||
|
var minEntropy float64
|
||||||
|
if len(password) == 0 {
|
||||||
|
minEntropy = float64(0)
|
||||||
|
} else {
|
||||||
|
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)}
|
||||||
|
|
||||||
|
}
|
||||||
|
func get(a []float64, i int) float64 {
|
||||||
|
if i < 0 || i >= len(a) {
|
||||||
|
return float64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
return a[i]
|
||||||
|
}
|
||||||
|
func calcBruteforceCardinality(password string) int {
|
||||||
|
lower, upper, digits, symbols := 0, 0, 0, 0
|
||||||
|
|
||||||
|
for _, char := range password {
|
||||||
|
if unicode.IsLower(char) {
|
||||||
|
lower = 26
|
||||||
|
} else if unicode.IsDigit(char) {
|
||||||
|
digits = 10
|
||||||
|
} else if unicode.IsUpper(char) {
|
||||||
|
upper = 26
|
||||||
|
} else {
|
||||||
|
symbols = 33
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cardinality := lower + upper + digits + symbols
|
||||||
|
return cardinality
|
||||||
|
}
|
||||||
|
|
||||||
|
func calcEntropy(match match.Match) float64 {
|
||||||
|
if match.Entropy > float64(0) {
|
||||||
|
return match.Entropy
|
||||||
|
}
|
||||||
|
|
||||||
|
var entropy float64
|
||||||
|
if match.Pattern == "dictionary" {
|
||||||
|
entropy = dictionaryEntropy(match)
|
||||||
|
} else if match.Pattern == "spatial" {
|
||||||
|
entropy = spatialEntropy(match)
|
||||||
|
}
|
||||||
|
|
||||||
|
match.Entropy = entropy
|
||||||
|
//TODO finish implement this. . . this looks to be the meat and potatoes of the calculation
|
||||||
|
return match.Entropy
|
||||||
|
}
|
||||||
|
|
||||||
|
func dictionaryEntropy(match match.Match) float64 {
|
||||||
|
baseEntropy := math.Log2(match.Rank)
|
||||||
|
upperCaseEntropy := extraUpperCaseEntropy(match)
|
||||||
|
//TODO: L33t
|
||||||
|
return baseEntropy + upperCaseEntropy
|
||||||
|
}
|
||||||
|
|
||||||
|
func spatialEntropy(match match.Match) float64 {
|
||||||
|
var s, d float64
|
||||||
|
if match.DictionaryName == "qwerty" || match.DictionaryName == "dvorak" {
|
||||||
|
s = float64(matching.KEYBOARD_STARTING_POSITIONS)
|
||||||
|
d = matching.KEYBOARD_AVG_DEGREE
|
||||||
|
} else {
|
||||||
|
s = float64(matching.KEYPAD_STARTING_POSITIONS)
|
||||||
|
d = matching.KEYPAD_AVG_DEGREE
|
||||||
|
}
|
||||||
|
|
||||||
|
possibilities := float64(0)
|
||||||
|
|
||||||
|
lenght := float64(len(match.Token))
|
||||||
|
t := match.Turns
|
||||||
|
|
||||||
|
//TODO: Should this be <= or just < ?
|
||||||
|
//Estimate the number of possible patterns w/ lenght L or less with t turns or less
|
||||||
|
for i := float64(2); i <= lenght + 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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entropy := math.Log2(possibilities)
|
||||||
|
|
||||||
|
//add extra entropu for shifted keys. ( % instead of 5 A instead of a)
|
||||||
|
//Math is similar to extra entropy for uppercase letters in dictionary matches.
|
||||||
|
|
||||||
|
if S := float64(match.ShiftedCount); S > float64(0) {
|
||||||
|
possibilities = float64(0)
|
||||||
|
U := lenght - S
|
||||||
|
|
||||||
|
for i := float64(0); i < math.Min(S, U) + 1; i++ {
|
||||||
|
possibilities += zxcvbn_math.NChoseK(S + U, i)
|
||||||
|
}
|
||||||
|
|
||||||
|
entropy += math.Log2(possibilities)
|
||||||
|
}
|
||||||
|
|
||||||
|
return entropy
|
||||||
|
}
|
||||||
|
func extraUpperCaseEntropy(match match.Match) float64 {
|
||||||
|
word := match.Token
|
||||||
|
|
||||||
|
allLower := true
|
||||||
|
|
||||||
|
for _, char := range word {
|
||||||
|
if unicode.IsUpper(char) {
|
||||||
|
allLower = false
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if allLower {
|
||||||
|
return float64(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
//a capitalized word is the most common capitalization scheme,
|
||||||
|
//so it only doubles the search space (uncapitalized + capitalized): 1 extra bit of entropy.
|
||||||
|
//allcaps and end-capitalized are common enough too, underestimate as 1 extra bit to be safe.
|
||||||
|
|
||||||
|
for _, regex := range []string{START_UPPER, END_UPPER, ALL_UPPER} {
|
||||||
|
matcher := regexp.MustCompile(regex)
|
||||||
|
|
||||||
|
if matcher.MatchString(word) {
|
||||||
|
return float64(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//Otherwise calculate the number of ways to capitalize U+L uppercase+lowercase letters with U uppercase letters or
|
||||||
|
//less. Or, if there's more uppercase than lower (for e.g. PASSwORD), the number of ways to lowercase U+L letters
|
||||||
|
//with L lowercase letters or less.
|
||||||
|
|
||||||
|
countUpper, countLower := float64(0), float64(0)
|
||||||
|
for _, char := range word {
|
||||||
|
if unicode.IsUpper(char) {
|
||||||
|
countUpper++
|
||||||
|
} else if unicode.IsLower(char) {
|
||||||
|
countLower++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
totalLenght := countLower + countUpper
|
||||||
|
var possibililities float64
|
||||||
|
|
||||||
|
for i := float64(0); i <= math.Min(countUpper, countLower); i++ {
|
||||||
|
possibililities += float64(zxcvbn_math.NChoseK(totalLenght, i))
|
||||||
|
}
|
||||||
|
|
||||||
|
if possibililities < 1 {
|
||||||
|
return float64(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return float64(math.Log2(possibililities))
|
||||||
|
}
|
||||||
|
|
||||||
|
func entropyToCrackTime(entropy float64) float64 {
|
||||||
|
crackTime := (0.5 * math.Pow(float64(2), entropy)) * SECONDS_PER_GUESS
|
||||||
|
|
||||||
|
return crackTime
|
||||||
|
}
|
||||||
|
|
||||||
|
func roundToXDigits(number float64, digits int) float64 {
|
||||||
|
return zxcvbn_math.Round(number, .5, digits)
|
||||||
|
}
|
||||||
|
|
||||||
|
func displayTime(seconds float64) string {
|
||||||
|
formater := "%d %s"
|
||||||
|
minute := float64(60)
|
||||||
|
hour := minute * float64(60)
|
||||||
|
day := hour * float64(24)
|
||||||
|
month := day * float64(31)
|
||||||
|
year := month * float64(12)
|
||||||
|
century := year * float64(100)
|
||||||
|
|
||||||
|
if seconds < minute {
|
||||||
|
return "instant"
|
||||||
|
} else if seconds < hour {
|
||||||
|
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / minute)), "minutes")
|
||||||
|
} else if seconds < day {
|
||||||
|
return fmt.Sprintf(formater, (1 + math.Ceil(seconds / hour)), "hours")
|
||||||
|
} else if seconds < month {
|
||||||
|
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")
|
||||||
|
} else {
|
||||||
|
return "centuries"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func crackTimeToScore(seconds float64) int {
|
||||||
|
if seconds < math.Pow(10, 2) {
|
||||||
|
return 0
|
||||||
|
} else if seconds < math.Pow(10, 4) {
|
||||||
|
return 1
|
||||||
|
} else if seconds < math.Pow(10, 6) {
|
||||||
|
return 2
|
||||||
|
} else if seconds < math.Pow(10, 8) {
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
return 4
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
package math
|
package zxcvbn_math
|
||||||
|
import "math"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,22 +8,34 @@ I am surprised that I have to define these. . . Maybe i just didn't look hard en
|
||||||
*/
|
*/
|
||||||
|
|
||||||
//http://blog.plover.com/math/choose.html
|
//http://blog.plover.com/math/choose.html
|
||||||
func NChoseK(n, k uint) uint64 {
|
func NChoseK(n, k float64) float64 {
|
||||||
uN := uint64(n)
|
if k > n {
|
||||||
uK := uint64(k)
|
|
||||||
if uK > uN {
|
|
||||||
return 0
|
return 0
|
||||||
} else if uK == 0 {
|
} else if k == 0 {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
var r uint64 = 1
|
var r float64 = 1
|
||||||
|
|
||||||
for d := uint64(1) ; d <= uK; d++ {
|
for d := float64(1); d <= k; d++ {
|
||||||
r *= uN
|
r *= n
|
||||||
r /= d
|
r /= d
|
||||||
uN--
|
n--
|
||||||
}
|
}
|
||||||
|
|
||||||
return r
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func Round(val float64, roundOn float64, places int) (newVal float64) {
|
||||||
|
var round float64
|
||||||
|
pow := math.Pow(10, float64(places))
|
||||||
|
digit := pow * val
|
||||||
|
_, div := math.Modf(digit)
|
||||||
|
if div >= roundOn {
|
||||||
|
round = math.Ceil(digit)
|
||||||
|
} else {
|
||||||
|
round = math.Floor(digit)
|
||||||
|
}
|
||||||
|
newVal = round / pow
|
||||||
|
return
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package math
|
package zxcvbn_math
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
18
zxcvbn.go
18
zxcvbn.go
|
@ -3,9 +3,23 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"zxcvbn-go/matching"
|
"zxcvbn-go/matching"
|
||||||
|
"zxcvbn-go/scoring"
|
||||||
|
"time"
|
||||||
|
"zxcvbn-go/utils/math"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Start")
|
password :="qw@!abcdPLSB$6D"
|
||||||
fmt.Println(matching.SpatialMatch("qw@!andghjandfTandftg"))
|
fmt.Println(PasswordStrength(password, nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
func PasswordStrength(password string, userInputs []string) scoring.MinEntropyMatch {
|
||||||
|
start := time.Now()
|
||||||
|
matches := matching.Omnimatch(password, userInputs)
|
||||||
|
result := scoring.MinimumEntropyMatchSequence(password, matches)
|
||||||
|
end := time.Now()
|
||||||
|
|
||||||
|
calcTime := end.Nanosecond() - start.Nanosecond()
|
||||||
|
result.CalcTime = zxcvbn_math.Round(float64(calcTime)*time.Nanosecond.Seconds(), .5, 3)
|
||||||
|
return result
|
||||||
}
|
}
|
Loading…
Reference in New Issue