arjunyel 8 лет назад
Родитель
Сommit
2d3bcc7e91
23 измененных файлов с 1600 добавлено и 16 удалено
  1. 25 0
      Gopkg.lock
  2. 34 0
      Gopkg.toml
  3. 22 16
      README.md
  4. 2 0
      vendor/github.com/nbutton23/zxcvbn-go/.gitignore
  5. 27 0
      vendor/github.com/nbutton23/zxcvbn-go/Gopkg.lock
  6. 26 0
      vendor/github.com/nbutton23/zxcvbn-go/Gopkg.toml
  7. 20 0
      vendor/github.com/nbutton23/zxcvbn-go/LICENSE.txt
  8. 78 0
      vendor/github.com/nbutton23/zxcvbn-go/README.md
  9. 96 0
      vendor/github.com/nbutton23/zxcvbn-go/adjacency/adjcmartix.go
  10. 79 0
      vendor/github.com/nbutton23/zxcvbn-go/data/bindata.go
  11. 215 0
      vendor/github.com/nbutton23/zxcvbn-go/entropy/entropyCalculator.go
  12. 47 0
      vendor/github.com/nbutton23/zxcvbn-go/frequency/frequency.go
  13. 40 0
      vendor/github.com/nbutton23/zxcvbn-go/match/match.go
  14. 204 0
      vendor/github.com/nbutton23/zxcvbn-go/matching/dateMatchers.go
  15. 54 0
      vendor/github.com/nbutton23/zxcvbn-go/matching/dictionaryMatch.go
  16. 75 0
      vendor/github.com/nbutton23/zxcvbn-go/matching/leet.go
  17. 87 0
      vendor/github.com/nbutton23/zxcvbn-go/matching/matching.go
  18. 66 0
      vendor/github.com/nbutton23/zxcvbn-go/matching/repeatMatch.go
  19. 75 0
      vendor/github.com/nbutton23/zxcvbn-go/matching/sequenceMatch.go
  20. 87 0
      vendor/github.com/nbutton23/zxcvbn-go/matching/spatialMatch.go
  21. 180 0
      vendor/github.com/nbutton23/zxcvbn-go/scoring/scoring.go
  22. 40 0
      vendor/github.com/nbutton23/zxcvbn-go/utils/math/mathutils.go
  23. 21 0
      vendor/github.com/nbutton23/zxcvbn-go/zxcvbn.go

+ 25 - 0
Gopkg.lock

@@ -0,0 +1,25 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+  name = "github.com/nbutton23/zxcvbn-go"
+  packages = [
+    ".",
+    "adjacency",
+    "data",
+    "entropy",
+    "frequency",
+    "match",
+    "matching",
+    "scoring",
+    "utils/math"
+  ]
+  revision = "eafdab6b0663b4b528c35975c8b0e78be6e25261"
+  version = "v0.1"
+
+[solve-meta]
+  analyzer-name = "dep"
+  analyzer-version = 1
+  inputs-digest = "23def62162c3bea7fbee673f94e6a5409c690650f3b1c40ffb6a1e257b2f271c"
+  solver-name = "gps-cdcl"
+  solver-version = 1

+ 34 - 0
Gopkg.toml

@@ -0,0 +1,34 @@
+# Gopkg.toml example
+#
+# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+#   name = "github.com/user/project"
+#   version = "1.0.0"
+#
+# [[constraint]]
+#   name = "github.com/user/project2"
+#   branch = "dev"
+#   source = "github.com/myfork/project2"
+#
+# [[override]]
+#   name = "github.com/x/y"
+#   version = "2.4.0"
+#
+# [prune]
+#   non-go = false
+#   go-tests = true
+#   unused-packages = true
+
+
+[[constraint]]
+  name = "github.com/nbutton23/zxcvbn-go"
+  version = "0.1.0"
+
+[prune]
+  go-tests = true
+  unused-packages = true

+ 22 - 16
README.md

@@ -1,26 +1,32 @@
 # GitLeaks
-#### Check git repos for secrets and keys
 
-##### Features:
- * Search all commits on all branches in topological order
- * Regex/Entropy checks
+## Check git repos for secrets and keys
 
-##### Installing:
-```
-$ go get github.com/zricethezav/gitleaks
+### Features
+
+* Search all commits on all branches in topological order
+* Regex/Entropy checks
+
+#### Installing
+
+```bash
+go get -u github.com/zricethezav/gitleaks
 ```
 
-##### Usage and Explanation: 
+#### Usage and Explanation
+
 ![Alt Text](https://github.com/zricethezav/gifs/blob/master/gitleaks.gif)
+
+```sh
+./gitleaks {git url}
 ```
-$ ./gitleaks {git url}
-```
-This will clone the target `{git url}` and run a diff on all commits. A report will be output to `{repo_name}.json` 
-Gitleaks scans all lines of all commit diffs and checks if there are any regular expression matches. The regexs are defined in `main.go`. For example if a line in a commit diff like `AWS_KEY='AKAI...'` exists then the value after the assignment operator will be checked for entropy. If the value is above a certain entropy threshold then we assume that the line contains a key/secret. Work largely based on  https://people.eecs.berkeley.edu/~rohanpadhye/files/key_leaks-msr15.pdf
 
+This will clone the target `{git url}` and run a diff on all commits. A report will be output to `{repo_name}.json`
+Gitleaks scans all lines of all commit diffs and checks if there are any regular expression matches. The regexs are defined in `main.go`. For example if a line in a commit diff like `AWS_KEY='AKAI...'` exists then the value after the assignment operator will be checked for entropy. If the value is above a certain entropy threshold then we assume that the line contains a key/secret. Work largely based on  [https://people.eecs.berkeley.edu/~rohanpadhye/files/key_leaks-msr15.pdf](https://people.eecs.berkeley.edu/~rohanpadhye/files/key_leaks-msr15.pdf)
 
 #### TODO
-- Specify a target branch
-- Support for custom regex
-- Filter regex
-- Modify entropy cutoff
+
+* Specify a target branch
+* Support for custom regex
+* Filter regex
+* Modify entropy cutoff

+ 2 - 0
vendor/github.com/nbutton23/zxcvbn-go/.gitignore

@@ -0,0 +1,2 @@
+zxcvbn
+debug.test

+ 27 - 0
vendor/github.com/nbutton23/zxcvbn-go/Gopkg.lock

@@ -0,0 +1,27 @@
+# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
+
+
+[[projects]]
+  name = "github.com/davecgh/go-spew"
+  packages = ["spew"]
+  revision = "346938d642f2ec3594ed81d874461961cd0faa76"
+  version = "v1.1.0"
+
+[[projects]]
+  name = "github.com/pmezard/go-difflib"
+  packages = ["difflib"]
+  revision = "792786c7400a136282c1664665ae0a8db921c6c2"
+  version = "v1.0.0"
+
+[[projects]]
+  name = "github.com/stretchr/testify"
+  packages = ["assert"]
+  revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
+  version = "v1.1.4"
+
+[solve-meta]
+  analyzer-name = "dep"
+  analyzer-version = 1
+  inputs-digest = "b750880cdc8ce044e6f9bf3b331d8a392471c328107b8c3d42e3e11022d76858"
+  solver-name = "gps-cdcl"
+  solver-version = 1

+ 26 - 0
vendor/github.com/nbutton23/zxcvbn-go/Gopkg.toml

@@ -0,0 +1,26 @@
+
+# Gopkg.toml example
+#
+# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
+# for detailed Gopkg.toml documentation.
+#
+# required = ["github.com/user/thing/cmd/thing"]
+# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
+#
+# [[constraint]]
+#   name = "github.com/user/project"
+#   version = "1.0.0"
+#
+# [[constraint]]
+#   name = "github.com/user/project2"
+#   branch = "dev"
+#   source = "github.com/myfork/project2"
+#
+# [[override]]
+#  name = "github.com/x/y"
+#  version = "2.4.0"
+
+
+[[constraint]]
+  name = "github.com/stretchr/testify"
+  version = "1.1.4"

+ 20 - 0
vendor/github.com/nbutton23/zxcvbn-go/LICENSE.txt

@@ -0,0 +1,20 @@
+Copyright (c) Nathan Button
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 78 - 0
vendor/github.com/nbutton23/zxcvbn-go/README.md

@@ -0,0 +1,78 @@
+This is a goLang port of python-zxcvbn and [zxcvbn](https://github.com/dropbox/zxcvbn), which are python and JavaScript password strength
+generators. zxcvbn attempts to give sound password advice through pattern
+matching and conservative entropy calculations. It finds 10k common passwords,
+common American names and surnames, common English words, and common patterns
+like dates, repeats (aaa), sequences (abcd), and QWERTY patterns.
+
+Please refer to http://tech.dropbox.com/?p=165 for the full details and
+motivation behind zxcbvn. The source code for the original JavaScript (well,
+actually CoffeeScript) implementation can be found at:
+
+https://github.com/lowe/zxcvbn
+
+Python at:
+
+https://github.com/dropbox/python-zxcvbn
+
+For full motivation, see:
+
+http://tech.dropbox.com/?p=165
+
+------------------------------------------------------------------------
+Use
+------------------------------------------------------------------------
+
+The zxcvbn module has the public method PasswordStrength() function. Import zxcvbn, and
+call PasswordStrength(password string, userInputs []string).  The function will return a
+result dictionary with the following keys:
+
+Entropy            # bits
+
+CrackTime         # estimation of actual crack time, in seconds.
+
+CrackTimeDisplay # same crack time, as a friendlier string:
+                   # "instant", "6 minutes", "centuries", etc.
+
+Score              # [0,1,2,3,4] if crack time is less than
+                   # [10^2, 10^4, 10^6, 10^8, Infinity].
+                   # (useful for implementing a strength bar.)
+
+MatchSequence     # the list of patterns that zxcvbn based the
+                   # entropy calculation on.
+
+CalcTime   # how long it took to calculate an answer,
+                   # in milliseconds. usually only a few ms.
+
+The userInputs argument is an splice of strings that zxcvbn
+will add to its internal dictionary. This can be whatever list of
+strings you like, but is meant for user inputs from other fields of the
+form, like name and email. That way a password that includes the user's
+personal info can be heavily penalized. This list is also good for
+site-specific vocabulary.
+
+Bug reports and pull requests welcome!
+
+------------------------------------------------------------------------
+Project Status
+------------------------------------------------------------------------
+
+Use zxcvbn_test.go to check how close to feature parity the project is.
+
+------------------------------------------------------------------------
+Acknowledgment
+------------------------------------------------------------------------
+
+Thanks to Dan Wheeler (https://github.com/lowe) for the CoffeeScript implementation
+(see above.) To repeat his outside acknowledgements (which remain useful, as always):
+
+Many thanks to Mark Burnett for releasing his 10k top passwords list:
+http://xato.net/passwords/more-top-worst-passwords
+and for his 2006 book,
+"Perfect Passwords: Selection, Protection, Authentication"
+
+Huge thanks to Wiktionary contributors for building a frequency list
+of English as used in television and movies:
+http://en.wiktionary.org/wiki/Wiktionary:Frequency_lists
+
+Last but not least, big thanks to xkcd :)
+https://xkcd.com/936/

+ 96 - 0
vendor/github.com/nbutton23/zxcvbn-go/adjacency/adjcmartix.go

@@ -0,0 +1,96 @@
+package adjacency
+
+import (
+	"encoding/json"
+	"log"
+	//	"fmt"
+	"github.com/nbutton23/zxcvbn-go/data"
+)
+
+type AdjacencyGraph struct {
+	Graph         map[string][]string
+	averageDegree float64
+	Name          string
+}
+
+var AdjacencyGph = make(map[string]AdjacencyGraph)
+
+func init() {
+	AdjacencyGph["qwerty"] = BuildQwerty()
+	AdjacencyGph["dvorak"] = BuildDvorak()
+	AdjacencyGph["keypad"] = BuildKeypad()
+	AdjacencyGph["macKeypad"] = BuildMacKeypad()
+	AdjacencyGph["l33t"] = BuildLeet()
+}
+
+func BuildQwerty() AdjacencyGraph {
+	data, err := zxcvbn_data.Asset("data/Qwerty.json")
+	if err != nil {
+		panic("Can't find asset")
+	}
+	return GetAdjancencyGraphFromFile(data, "qwerty")
+}
+func BuildDvorak() AdjacencyGraph {
+	data, err := zxcvbn_data.Asset("data/Dvorak.json")
+	if err != nil {
+		panic("Can't find asset")
+	}
+	return GetAdjancencyGraphFromFile(data, "dvorak")
+}
+func BuildKeypad() AdjacencyGraph {
+	data, err := zxcvbn_data.Asset("data/Keypad.json")
+	if err != nil {
+		panic("Can't find asset")
+	}
+	return GetAdjancencyGraphFromFile(data, "keypad")
+}
+func BuildMacKeypad() AdjacencyGraph {
+	data, err := zxcvbn_data.Asset("data/MacKeypad.json")
+	if err != nil {
+		panic("Can't find asset")
+	}
+	return GetAdjancencyGraphFromFile(data, "mac_keypad")
+}
+func BuildLeet() AdjacencyGraph {
+	data, err := zxcvbn_data.Asset("data/L33t.json")
+	if err != nil {
+		panic("Can't find asset")
+	}
+	return GetAdjancencyGraphFromFile(data, "keypad")
+}
+
+func GetAdjancencyGraphFromFile(data []byte, name string) AdjacencyGraph {
+
+	var graph AdjacencyGraph
+	err := json.Unmarshal(data, &graph)
+	if err != nil {
+		log.Fatal(err)
+	}
+	graph.Name = name
+	return graph
+}
+
+//on qwerty, 'g' has degree 6, being adjacent to 'ftyhbv'. '\' has degree 1.
+//this calculates the average over all keys.
+//TODO double check that i ported this correctly scoring.coffee ln 5
+func (adjGrp AdjacencyGraph) CalculateAvgDegree() float64 {
+	if adjGrp.averageDegree != float64(0) {
+		return adjGrp.averageDegree
+	}
+	var avg float64
+	var count float64
+	for _, value := range adjGrp.Graph {
+
+		for _, char := range value {
+			if char != "" || char != " " {
+				avg += float64(len(char))
+				count++
+			}
+		}
+
+	}
+
+	adjGrp.averageDegree = avg / count
+
+	return adjGrp.averageDegree
+}

Разница между файлами не показана из-за своего большого размера
+ 79 - 0
vendor/github.com/nbutton23/zxcvbn-go/data/bindata.go


+ 215 - 0
vendor/github.com/nbutton23/zxcvbn-go/entropy/entropyCalculator.go

@@ -0,0 +1,215 @@
+package entropy
+
+import (
+	"github.com/nbutton23/zxcvbn-go/adjacency"
+	"github.com/nbutton23/zxcvbn-go/match"
+	"github.com/nbutton23/zxcvbn-go/utils/math"
+	"math"
+	"regexp"
+	"unicode"
+)
+
+const (
+	START_UPPER string = `^[A-Z][^A-Z]+$`
+	END_UPPER   string = `^[^A-Z]+[A-Z]$'`
+	ALL_UPPER   string = `^[A-Z]+$`
+	NUM_YEARS          = float64(119) // years match against 1900 - 2019
+	NUM_MONTHS         = float64(12)
+	NUM_DAYS           = float64(31)
+)
+
+var (
+	KEYPAD_STARTING_POSITIONS = len(adjacency.AdjacencyGph["keypad"].Graph)
+	KEYPAD_AVG_DEGREE         = adjacency.AdjacencyGph["keypad"].CalculateAvgDegree()
+)
+
+func DictionaryEntropy(match match.Match, rank float64) float64 {
+	baseEntropy := math.Log2(rank)
+	upperCaseEntropy := extraUpperCaseEntropy(match)
+	//TODO: L33t
+	return baseEntropy + upperCaseEntropy
+}
+
+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 SpatialEntropy(match match.Match, turns int, shiftCount int) float64 {
+	var s, d float64
+	if match.DictionaryName == "qwerty" || match.DictionaryName == "dvorak" {
+		//todo: verify qwerty and dvorak have the same length and degree
+		s = float64(len(adjacency.BuildQwerty().Graph))
+		d = adjacency.BuildQwerty().CalculateAvgDegree()
+	} else {
+		s = float64(KEYPAD_STARTING_POSITIONS)
+		d = KEYPAD_AVG_DEGREE
+	}
+
+	possibilities := float64(0)
+
+	length := float64(len(match.Token))
+
+	//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(turns), 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(shiftCount); S > float64(0) {
+		possibilities = float64(0)
+		U := length - 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 RepeatEntropy(match match.Match) float64 {
+	cardinality := CalcBruteForceCardinality(match.Token)
+	entropy := math.Log2(cardinality * float64(len(match.Token)))
+
+	return entropy
+}
+
+//TODO: Validate against python
+func CalcBruteForceCardinality(password string) float64 {
+	lower, upper, digits, symbols := float64(0), float64(0), float64(0), float64(0)
+
+	for _, char := range password {
+		if unicode.IsLower(char) {
+			lower = float64(26)
+		} else if unicode.IsDigit(char) {
+			digits = float64(10)
+		} else if unicode.IsUpper(char) {
+			upper = float64(26)
+		} else {
+			symbols = float64(33)
+		}
+	}
+
+	cardinality := lower + upper + digits + symbols
+	return cardinality
+}
+
+func SequenceEntropy(match match.Match, dictionaryLength int, ascending bool) float64 {
+	firstChar := match.Token[0]
+	baseEntropy := float64(0)
+	if string(firstChar) == "a" || string(firstChar) == "1" {
+		baseEntropy = float64(0)
+	} else {
+		baseEntropy = math.Log2(float64(dictionaryLength))
+		//TODO: should this be just the first or any char?
+		if unicode.IsUpper(rune(firstChar)) {
+			baseEntropy++
+		}
+	}
+
+	if !ascending {
+		baseEntropy++
+	}
+	return baseEntropy + math.Log2(float64(len(match.Token)))
+}
+
+func ExtraLeetEntropy(match match.Match, password string) float64 {
+	var subsitutions float64
+	var unsub float64
+	subPassword := password[match.I:match.J]
+	for index, char := range subPassword {
+		if string(char) != string(match.Token[index]) {
+			subsitutions++
+		} else {
+			//TODO: Make this only true for 1337 chars that are not subs?
+			unsub++
+		}
+	}
+
+	var possibilities float64
+
+	for i := float64(0); i <= math.Min(subsitutions, unsub)+1; i++ {
+		possibilities += zxcvbn_math.NChoseK(subsitutions+unsub, i)
+	}
+
+	if possibilities <= 1 {
+		return float64(1)
+	}
+	return math.Log2(possibilities)
+}
+
+func YearEntropy(dateMatch match.DateMatch) float64 {
+	return math.Log2(NUM_YEARS)
+}
+
+func DateEntropy(dateMatch match.DateMatch) float64 {
+	var entropy float64
+	if dateMatch.Year < 100 {
+		entropy = math.Log2(NUM_DAYS * NUM_MONTHS * 100)
+	} else {
+		entropy = math.Log2(NUM_DAYS * NUM_MONTHS * NUM_YEARS)
+	}
+
+	if dateMatch.Separator != "" {
+		entropy += 2 //add two bits for separator selection [/,-,.,etc]
+	}
+	return entropy
+}

+ 47 - 0
vendor/github.com/nbutton23/zxcvbn-go/frequency/frequency.go

@@ -0,0 +1,47 @@
+package frequency
+
+import (
+	"encoding/json"
+	"github.com/nbutton23/zxcvbn-go/data"
+	"log"
+)
+
+type FrequencyList struct {
+	Name string
+	List []string
+}
+
+var FrequencyLists = make(map[string]FrequencyList)
+
+func init() {
+	maleFilePath := getAsset("data/MaleNames.json")
+	femaleFilePath := getAsset("data/FemaleNames.json")
+	surnameFilePath := getAsset("data/Surnames.json")
+	englishFilePath := getAsset("data/English.json")
+	passwordsFilePath := getAsset("data/Passwords.json")
+
+	FrequencyLists["MaleNames"] = GetStringListFromAsset(maleFilePath, "MaleNames")
+	FrequencyLists["FemaleNames"] = GetStringListFromAsset(femaleFilePath, "FemaleNames")
+	FrequencyLists["Surname"] = GetStringListFromAsset(surnameFilePath, "Surname")
+	FrequencyLists["English"] = GetStringListFromAsset(englishFilePath, "English")
+	FrequencyLists["Passwords"] = GetStringListFromAsset(passwordsFilePath, "Passwords")
+
+}
+func getAsset(name string) []byte {
+	data, err := zxcvbn_data.Asset(name)
+	if err != nil {
+		panic("Error getting asset " + name)
+	}
+
+	return data
+}
+func GetStringListFromAsset(data []byte, name string) FrequencyList {
+
+	var tempList FrequencyList
+	err := json.Unmarshal(data, &tempList)
+	if err != nil {
+		log.Fatal(err)
+	}
+	tempList.Name = name
+	return tempList
+}

+ 40 - 0
vendor/github.com/nbutton23/zxcvbn-go/match/match.go

@@ -0,0 +1,40 @@
+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
+	DictionaryName string
+	Entropy        float64
+}
+
+type DateMatch struct {
+	Pattern          string
+	I, J             int
+	Token            string
+	Separator        string
+	Day, Month, Year int64
+}
+
+type Matcher struct {
+	MatchingFunc func(password string) []Match
+	ID           string
+}

+ 204 - 0
vendor/github.com/nbutton23/zxcvbn-go/matching/dateMatchers.go

@@ -0,0 +1,204 @@
+package matching
+
+import (
+	"regexp"
+	"strconv"
+	"strings"
+
+	"github.com/nbutton23/zxcvbn-go/entropy"
+	"github.com/nbutton23/zxcvbn-go/match"
+)
+
+const (
+	DATESEP_MATCHER_NAME        = "DATESEP"
+	DATEWITHOUTSEP_MATCHER_NAME = "DATEWITHOUT"
+)
+
+func FilterDateSepMatcher(m match.Matcher) bool {
+	return m.ID == DATESEP_MATCHER_NAME
+}
+
+func FilterDateWithoutSepMatcher(m match.Matcher) bool {
+	return m.ID == DATEWITHOUTSEP_MATCHER_NAME
+}
+
+func checkDate(day, month, year int64) (bool, int64, int64, int64) {
+	if (12 <= month && month <= 31) && day <= 12 {
+		day, month = month, day
+	}
+
+	if day > 31 || month > 12 {
+		return false, 0, 0, 0
+	}
+
+	if !((1900 <= year && year <= 2019) || (0 <= year && year <= 99)) {
+		return false, 0, 0, 0
+	}
+
+	return true, day, month, year
+}
+
+func dateSepMatcher(password string) []match.Match {
+	dateMatches := dateSepMatchHelper(password)
+
+	var matches []match.Match
+	for _, dateMatch := range dateMatches {
+		match := match.Match{
+			I:              dateMatch.I,
+			J:              dateMatch.J,
+			Entropy:        entropy.DateEntropy(dateMatch),
+			DictionaryName: "date_match",
+			Token:          dateMatch.Token,
+		}
+
+		matches = append(matches, match)
+	}
+
+	return matches
+}
+func dateSepMatchHelper(password string) []match.DateMatch {
+
+	var matches []match.DateMatch
+
+	matcher := regexp.MustCompile(DATE_RX_YEAR_SUFFIX)
+	for _, v := range matcher.FindAllString(password, len(password)) {
+		splitV := matcher.FindAllStringSubmatch(v, len(v))
+		i := strings.Index(password, v)
+		j := i + len(v)
+		day, _ := strconv.ParseInt(splitV[0][4], 10, 16)
+		month, _ := strconv.ParseInt(splitV[0][2], 10, 16)
+		year, _ := strconv.ParseInt(splitV[0][6], 10, 16)
+		match := match.DateMatch{Day: day, Month: month, Year: year, Separator: splitV[0][5], I: i, J: j, Token: password[i:j]}
+		matches = append(matches, match)
+	}
+
+	matcher = regexp.MustCompile(DATE_RX_YEAR_PREFIX)
+	for _, v := range matcher.FindAllString(password, len(password)) {
+		splitV := matcher.FindAllStringSubmatch(v, len(v))
+		i := strings.Index(password, v)
+		j := i + len(v)
+		day, _ := strconv.ParseInt(splitV[0][4], 10, 16)
+		month, _ := strconv.ParseInt(splitV[0][6], 10, 16)
+		year, _ := strconv.ParseInt(splitV[0][2], 10, 16)
+		match := match.DateMatch{Day: day, Month: month, Year: year, Separator: splitV[0][5], I: i, J: j, Token: password[i:j]}
+		matches = append(matches, match)
+	}
+
+	var out []match.DateMatch
+	for _, match := range matches {
+		if valid, day, month, year := checkDate(match.Day, match.Month, match.Year); valid {
+			match.Pattern = "date"
+			match.Day = day
+			match.Month = month
+			match.Year = year
+			out = append(out, match)
+		}
+	}
+	return out
+
+}
+
+type DateMatchCandidate struct {
+	DayMonth string
+	Year     string
+	I, J     int
+}
+
+type DateMatchCandidateTwo struct {
+	Day   string
+	Month string
+	Year  string
+	I, J  int
+}
+
+func dateWithoutSepMatch(password string) []match.Match {
+	dateMatches := dateWithoutSepMatchHelper(password)
+
+	var matches []match.Match
+	for _, dateMatch := range dateMatches {
+		match := match.Match{
+			I:              dateMatch.I,
+			J:              dateMatch.J,
+			Entropy:        entropy.DateEntropy(dateMatch),
+			DictionaryName: "date_match",
+			Token:          dateMatch.Token,
+		}
+
+		matches = append(matches, match)
+	}
+
+	return matches
+}
+
+//TODO Has issues with 6 digit dates
+func dateWithoutSepMatchHelper(password string) (matches []match.DateMatch) {
+	matcher := regexp.MustCompile(DATE_WITHOUT_SEP_MATCH)
+	for _, v := range matcher.FindAllString(password, len(password)) {
+		i := strings.Index(password, v)
+		j := i + len(v)
+		length := len(v)
+		lastIndex := length - 1
+		var candidatesRoundOne []DateMatchCandidate
+
+		if length <= 6 {
+			//2-digit year prefix
+			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))
+		}
+		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-3], v[lastIndex-3:], i, j))
+		}
+
+		var candidatesRoundTwo []DateMatchCandidateTwo
+		for _, c := range candidatesRoundOne {
+			if len(c.DayMonth) == 2 {
+				candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:0], c.DayMonth[1:1], c.Year, c.I, c.J))
+			} else if len(c.DayMonth) == 3 {
+				candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:2], c.DayMonth[2:2], c.Year, c.I, c.J))
+				candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:0], c.DayMonth[1:3], c.Year, c.I, c.J))
+			} else if len(c.DayMonth) == 4 {
+				candidatesRoundTwo = append(candidatesRoundTwo, buildDateMatchCandidateTwo(c.DayMonth[0:2], c.DayMonth[2:4], c.Year, c.I, c.J))
+			}
+		}
+
+		for _, candidate := range candidatesRoundTwo {
+			intDay, err := strconv.ParseInt(candidate.Day, 10, 16)
+			if err != nil {
+				continue
+			}
+
+			intMonth, err := strconv.ParseInt(candidate.Month, 10, 16)
+
+			if err != nil {
+				continue
+			}
+
+			intYear, err := strconv.ParseInt(candidate.Year, 10, 16)
+			if err != nil {
+				continue
+			}
+
+			if ok, _, _, _ := checkDate(intDay, intMonth, intYear); ok {
+				matches = append(matches, match.DateMatch{Token: password, Pattern: "date", Day: intDay, Month: intMonth, Year: intYear, I: i, J: j})
+			}
+
+		}
+	}
+
+	return matches
+}
+
+func buildDateMatchCandidate(dayMonth, year string, i, j int) DateMatchCandidate {
+	return DateMatchCandidate{DayMonth: dayMonth, Year: year, I: i, J: j}
+}
+
+func buildDateMatchCandidateTwo(day, month string, year string, i, j int) DateMatchCandidateTwo {
+
+	return DateMatchCandidateTwo{Day: day, Month: month, Year: year, I: i, J: j}
+}

+ 54 - 0
vendor/github.com/nbutton23/zxcvbn-go/matching/dictionaryMatch.go

@@ -0,0 +1,54 @@
+package matching
+
+import (
+	"github.com/nbutton23/zxcvbn-go/entropy"
+	"github.com/nbutton23/zxcvbn-go/match"
+	"strings"
+)
+
+func buildDictMatcher(dictName string, rankedDict map[string]int) func(password string) []match.Match {
+	return func(password string) []match.Match {
+		matches := dictionaryMatch(password, dictName, rankedDict)
+		for _, v := range matches {
+			v.DictionaryName = dictName
+		}
+		return matches
+	}
+
+}
+
+func dictionaryMatch(password string, dictionaryName string, rankedDict map[string]int) []match.Match {
+	length := len(password)
+	var results []match.Match
+	pwLower := strings.ToLower(password)
+
+	for i := 0; i < length; i++ {
+		for j := i; j < length; j++ {
+			word := pwLower[i : j+1]
+			if val, ok := rankedDict[word]; ok {
+				matchDic := match.Match{Pattern: "dictionary",
+					DictionaryName: dictionaryName,
+					I:              i,
+					J:              j,
+					Token:          password[i : j+1],
+				}
+				matchDic.Entropy = entropy.DictionaryEntropy(matchDic, float64(val))
+
+				results = append(results, matchDic)
+			}
+		}
+	}
+
+	return results
+}
+
+func buildRankedDict(unrankedList []string) map[string]int {
+
+	result := make(map[string]int)
+
+	for i, v := range unrankedList {
+		result[strings.ToLower(v)] = i + 1
+	}
+
+	return result
+}

+ 75 - 0
vendor/github.com/nbutton23/zxcvbn-go/matching/leet.go

@@ -0,0 +1,75 @@
+package matching
+
+import (
+	"strings"
+
+	"github.com/nbutton23/zxcvbn-go/entropy"
+	"github.com/nbutton23/zxcvbn-go/match"
+)
+
+const L33T_MATCHER_NAME = "l33t"
+
+func FilterL33tMatcher(m match.Matcher) bool {
+	return m.ID == L33T_MATCHER_NAME
+}
+
+func l33tMatch(password string) []match.Match {
+
+	substitutions := relevantL33tSubtable(password)
+
+	permutations := getAllPermutationsOfLeetSubstitutions(password, substitutions)
+
+	var matches []match.Match
+
+	for _, permutation := range permutations {
+		for _, mather := range DICTIONARY_MATCHERS {
+			matches = append(matches, mather.MatchingFunc(permutation)...)
+		}
+	}
+
+	for _, match := range matches {
+		match.Entropy += entropy.ExtraLeetEntropy(match, password)
+		match.DictionaryName = match.DictionaryName + "_3117"
+	}
+
+	return matches
+}
+
+func getAllPermutationsOfLeetSubstitutions(password string, substitutionsMap map[string][]string) []string {
+
+	var permutations []string
+
+	for index, char := range password {
+		for value, splice := range substitutionsMap {
+			for _, sub := range splice {
+				if string(char) == sub {
+					var permutation string
+					permutation = password[:index] + value + password[index+1:]
+
+					permutations = append(permutations, permutation)
+					if index < len(permutation) {
+						tempPermutations := getAllPermutationsOfLeetSubstitutions(permutation[index+1:], substitutionsMap)
+						for _, temp := range tempPermutations {
+							permutations = append(permutations, permutation[:index+1]+temp)
+						}
+
+					}
+				}
+			}
+		}
+	}
+
+	return permutations
+}
+
+func relevantL33tSubtable(password string) map[string][]string {
+	relevantSubs := make(map[string][]string)
+	for key, values := range L33T_TABLE.Graph {
+		for _, value := range values {
+			if strings.Contains(password, value) {
+				relevantSubs[key] = append(relevantSubs[key], value)
+			}
+		}
+	}
+	return relevantSubs
+}

+ 87 - 0
vendor/github.com/nbutton23/zxcvbn-go/matching/matching.go

@@ -0,0 +1,87 @@
+package matching
+
+import (
+	"sort"
+
+	"github.com/nbutton23/zxcvbn-go/adjacency"
+	"github.com/nbutton23/zxcvbn-go/frequency"
+	"github.com/nbutton23/zxcvbn-go/match"
+)
+
+var (
+	DICTIONARY_MATCHERS []match.Matcher
+	MATCHERS            []match.Matcher
+	ADJACENCY_GRAPHS    []adjacency.AdjacencyGraph
+	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_WITHOUT_SEP_MATCH string = `\d{4,8}`
+)
+
+func init() {
+	loadFrequencyList()
+}
+
+func Omnimatch(password string, userInputs []string, filters ...func(match.Matcher) bool) (matches []match.Match) {
+
+	//Can I run into the issue where nil is not equal to nil?
+	if DICTIONARY_MATCHERS == nil || ADJACENCY_GRAPHS == nil {
+		loadFrequencyList()
+	}
+
+	if userInputs != nil {
+		userInputMatcher := buildDictMatcher("user_inputs", buildRankedDict(userInputs))
+		matches = userInputMatcher(password)
+	}
+
+	for _, matcher := range MATCHERS {
+		shouldBeFiltered := false
+		for i := range filters {
+			if filters[i](matcher) {
+				shouldBeFiltered = true
+				break
+			}
+		}
+		if !shouldBeFiltered {
+			matches = append(matches, matcher.MatchingFunc(password)...)
+		}
+	}
+	sort.Sort(match.Matches(matches))
+	return matches
+}
+
+func loadFrequencyList() {
+
+	for n, list := range frequency.FrequencyLists {
+		DICTIONARY_MATCHERS = append(DICTIONARY_MATCHERS, match.Matcher{MatchingFunc: buildDictMatcher(n, buildRankedDict(list.List)), ID: n})
+	}
+
+	L33T_TABLE = adjacency.AdjacencyGph["l33t"]
+
+	ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["qwerty"])
+	ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["dvorak"])
+	ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["keypad"])
+	ADJACENCY_GRAPHS = append(ADJACENCY_GRAPHS, adjacency.AdjacencyGph["macKeypad"])
+
+	//l33tFilePath, _ := filepath.Abs("adjacency/L33t.json")
+	//L33T_TABLE = adjacency.GetAdjancencyGraphFromFile(l33tFilePath, "l33t")
+
+	SEQUENCES = make(map[string]string)
+	SEQUENCES["lower"] = "abcdefghijklmnopqrstuvwxyz"
+	SEQUENCES["upper"] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+	SEQUENCES["digits"] = "0123456789"
+
+	MATCHERS = append(MATCHERS, DICTIONARY_MATCHERS...)
+	MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: spatialMatch, ID: SPATIAL_MATCHER_NAME})
+	MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: repeatMatch, ID: REPEAT_MATCHER_NAME})
+	MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: sequenceMatch, ID: SEQUENCE_MATCHER_NAME})
+	MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: l33tMatch, ID: L33T_MATCHER_NAME})
+	MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: dateSepMatcher, ID: DATESEP_MATCHER_NAME})
+	MATCHERS = append(MATCHERS, match.Matcher{MatchingFunc: dateWithoutSepMatch, ID: DATEWITHOUTSEP_MATCHER_NAME})
+
+}

+ 66 - 0
vendor/github.com/nbutton23/zxcvbn-go/matching/repeatMatch.go

@@ -0,0 +1,66 @@
+package matching
+
+import (
+	"strings"
+
+	"github.com/nbutton23/zxcvbn-go/entropy"
+	"github.com/nbutton23/zxcvbn-go/match"
+)
+
+const REPEAT_MATCHER_NAME = "REPEAT"
+
+func FilterRepeatMatcher(m match.Matcher) bool {
+	return m.ID == REPEAT_MATCHER_NAME
+}
+
+func repeatMatch(password string) []match.Match {
+	var matches []match.Match
+
+	//Loop through password. if current == prev currentStreak++ else if currentStreak > 2 {buildMatch; currentStreak = 1} prev = current
+	var current, prev string
+	currentStreak := 1
+	var i int
+	var char rune
+	for i, char = range password {
+		current = string(char)
+		if i == 0 {
+			prev = current
+			continue
+		}
+
+		if strings.ToLower(current) == strings.ToLower(prev) {
+			currentStreak++
+
+		} else if currentStreak > 2 {
+			iPos := i - currentStreak
+			jPos := i - 1
+			matchRepeat := match.Match{
+				Pattern:        "repeat",
+				I:              iPos,
+				J:              jPos,
+				Token:          password[iPos : jPos+1],
+				DictionaryName: prev}
+			matchRepeat.Entropy = entropy.RepeatEntropy(matchRepeat)
+			matches = append(matches, matchRepeat)
+			currentStreak = 1
+		} else {
+			currentStreak = 1
+		}
+
+		prev = current
+	}
+
+	if currentStreak > 2 {
+		iPos := i - currentStreak + 1
+		jPos := i
+		matchRepeat := match.Match{
+			Pattern:        "repeat",
+			I:              iPos,
+			J:              jPos,
+			Token:          password[iPos : jPos+1],
+			DictionaryName: prev}
+		matchRepeat.Entropy = entropy.RepeatEntropy(matchRepeat)
+		matches = append(matches, matchRepeat)
+	}
+	return matches
+}

+ 75 - 0
vendor/github.com/nbutton23/zxcvbn-go/matching/sequenceMatch.go

@@ -0,0 +1,75 @@
+package matching
+
+import (
+	"strings"
+
+	"github.com/nbutton23/zxcvbn-go/entropy"
+	"github.com/nbutton23/zxcvbn-go/match"
+)
+
+const SEQUENCE_MATCHER_NAME = "SEQ"
+
+func FilterSequenceMatcher(m match.Matcher) bool {
+	return m.ID == SEQUENCE_MATCHER_NAME
+}
+
+func sequenceMatch(password string) []match.Match {
+	var matches []match.Match
+	for i := 0; i < len(password); {
+		j := i + 1
+		var seq string
+		var seqName string
+		seqDirection := 0
+		for seqCandidateName, seqCandidate := range SEQUENCES {
+			iN := strings.Index(seqCandidate, string(password[i]))
+			var jN int
+			if j < len(password) {
+				jN = strings.Index(seqCandidate, string(password[j]))
+			} else {
+				jN = -1
+			}
+
+			if iN > -1 && jN > -1 {
+				direction := jN - iN
+				if direction == 1 || direction == -1 {
+					seq = seqCandidate
+					seqName = seqCandidateName
+					seqDirection = direction
+					break
+				}
+			}
+
+		}
+
+		if seq != "" {
+			for {
+				var prevN, curN int
+				if j < len(password) {
+					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 {
+						matchSequence := match.Match{
+							Pattern:        "sequence",
+							I:              i,
+							J:              j - 1,
+							Token:          password[i:j],
+							DictionaryName: seqName,
+						}
+
+						matchSequence.Entropy = entropy.SequenceEntropy(matchSequence, len(seq), (seqDirection == 1))
+						matches = append(matches, matchSequence)
+					}
+					break
+				} else {
+					j += 1
+				}
+
+			}
+		}
+		i = j
+	}
+	return matches
+}

+ 87 - 0
vendor/github.com/nbutton23/zxcvbn-go/matching/spatialMatch.go

@@ -0,0 +1,87 @@
+package matching
+
+import (
+	"strings"
+
+	"github.com/nbutton23/zxcvbn-go/adjacency"
+	"github.com/nbutton23/zxcvbn-go/entropy"
+	"github.com/nbutton23/zxcvbn-go/match"
+)
+
+const SPATIAL_MATCHER_NAME = "SPATIAL"
+
+func FilterSpatialMatcher(m match.Matcher) bool {
+	return m.ID == SPATIAL_MATCHER_NAME
+}
+
+func spatialMatch(password string) (matches []match.Match) {
+	for _, graph := range ADJACENCY_GRAPHS {
+		if graph.Graph != nil {
+			matches = append(matches, spatialMatchHelper(password, graph)...)
+		}
+	}
+	return matches
+}
+
+func spatialMatchHelper(password string, graph adjacency.AdjacencyGraph) (matches []match.Match) {
+
+	for i := 0; i < len(password)-1; {
+		j := i + 1
+		lastDirection := -99 //an int that it should never be!
+		turns := 0
+		shiftedCount := 0
+
+		for {
+			prevChar := password[j-1]
+			found := false
+			foundDirection := -1
+			curDirection := -1
+			//My graphs seem to be wrong. . . and where the hell is qwerty
+			adjacents := graph.Graph[string(prevChar)]
+			//Consider growing pattern by one character if j hasn't gone over the edge
+			if j < len(password) {
+				curChar := password[j]
+				for _, adj := range adjacents {
+					curDirection += 1
+
+					if strings.Index(adj, string(curChar)) != -1 {
+						found = true
+						foundDirection = curDirection
+
+						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.
+							//for example, 'q' is adjacent to the entry '2@'. @ is shifted w/ index 1, 2 is unshifted.
+							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 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 {
+					matchSpc := match.Match{Pattern: "spatial", I: i, J: j - 1, Token: password[i:j], DictionaryName: graph.Name}
+					matchSpc.Entropy = entropy.SpatialEntropy(matchSpc, turns, shiftedCount)
+					matches = append(matches, matchSpc)
+				}
+				//. . . and then start a new search from the rest of the password
+				i = j
+				break
+			}
+		}
+
+	}
+	return matches
+}

+ 180 - 0
vendor/github.com/nbutton23/zxcvbn-go/scoring/scoring.go

@@ -0,0 +1,180 @@
+package scoring
+
+import (
+	"fmt"
+	"github.com/nbutton23/zxcvbn-go/entropy"
+	"github.com/nbutton23/zxcvbn-go/match"
+	"github.com/nbutton23/zxcvbn-go/utils/math"
+	"math"
+	"sort"
+)
+
+const (
+	START_UPPER string = `^[A-Z][^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
+	SECONDS_PER_GUESS float64 = SINGLE_GUESS / NUM_ATTACKERS
+)
+
+type MinEntropyMatch struct {
+	Password         string
+	Entropy          float64
+	MatchSequence    []match.Match
+	CrackTime        float64
+	CrackTimeDisplay string
+	Score            int
+	CalcTime         float64
+}
+
+/*
+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(entropy.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)
+			candidateEntropy := upTo + match.Entropy
+
+			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))
+
+	makeBruteForceMatch := 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, makeBruteForceMatch(k, i-1))
+		}
+		k = j + 1
+		matchSequenceCopy = append(matchSequenceCopy, match)
+	}
+
+	if k < len(password) {
+		matchSequenceCopy = append(matchSequenceCopy, makeBruteForceMatch(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 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 := "%.1f %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
+}

+ 40 - 0
vendor/github.com/nbutton23/zxcvbn-go/utils/math/mathutils.go

@@ -0,0 +1,40 @@
+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 {
+	if k > n {
+		return 0
+	} else if k == 0 {
+		return 1
+	}
+
+	var r float64 = 1
+
+	for d := float64(1); d <= k; d++ {
+		r *= n
+		r /= d
+		n--
+	}
+
+	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
+}

+ 21 - 0
vendor/github.com/nbutton23/zxcvbn-go/zxcvbn.go

@@ -0,0 +1,21 @@
+package zxcvbn
+
+import (
+	"time"
+
+	"github.com/nbutton23/zxcvbn-go/match"
+	"github.com/nbutton23/zxcvbn-go/matching"
+	"github.com/nbutton23/zxcvbn-go/scoring"
+	"github.com/nbutton23/zxcvbn-go/utils/math"
+)
+
+func PasswordStrength(password string, userInputs []string, filters ...func(match.Matcher) bool) scoring.MinEntropyMatch {
+	start := time.Now()
+	matches := matching.Omnimatch(password, userInputs, filters...)
+	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
+}

Некоторые файлы не были показаны из-за большого количества измененных файлов