Initial QSfera import
This commit is contained in:
+168
@@ -0,0 +1,168 @@
|
||||
package matching
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
// "github.com/trustelem/zxcvbn/entropy"
|
||||
"github.com/trustelem/zxcvbn/match"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type l33tMatch struct {
|
||||
dm dictionaryMatch
|
||||
table map[string][]string
|
||||
}
|
||||
|
||||
func (lm l33tMatch) Matches(password string) []*match.Match {
|
||||
matches := []*match.Match{}
|
||||
|
||||
substitutions := relevantSubtable(password, lm.table)
|
||||
|
||||
for _, sub := range enumerateLeetSubs(substitutions) {
|
||||
if len(sub) == 0 {
|
||||
break
|
||||
}
|
||||
subbedPassword := translate(password, sub)
|
||||
for _, m := range lm.dm.Matches(subbedPassword) {
|
||||
token := password[m.I : m.J+1]
|
||||
if len(token) <= 1 {
|
||||
// filter single-character l33t matches to reduce noise.
|
||||
// otherwise '1' matches 'i', '4' matches 'a', both very common English words
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.ToLower(token) == m.MatchedWord {
|
||||
continue // only return the matches that return an actual substitution
|
||||
}
|
||||
m.Sub = make(map[string]string)
|
||||
for subbed, chr := range sub {
|
||||
if strings.Contains(token, subbed) {
|
||||
m.Sub[subbed] = chr
|
||||
}
|
||||
}
|
||||
m.L33t = true
|
||||
m.Token = token
|
||||
matches = append(matches, m)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
match.Sort(matches)
|
||||
return matches
|
||||
}
|
||||
|
||||
func translate(password string, sub map[string]string) string {
|
||||
var res string
|
||||
for _, s := range password {
|
||||
if v, ok := sub[string(s)]; ok {
|
||||
res = res + v
|
||||
} else {
|
||||
res = res + string(s)
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
type kv struct {
|
||||
k string
|
||||
v string
|
||||
}
|
||||
|
||||
func dedup(subs [][]kv) [][]kv {
|
||||
var res [][]kv
|
||||
var b bytes.Buffer
|
||||
members := make(map[string]bool)
|
||||
for _, sub := range subs {
|
||||
sort.SliceStable(sub, func(i, j int) bool {
|
||||
return sub[i].k < sub[j].k
|
||||
})
|
||||
b.Reset()
|
||||
for _, x := range sub {
|
||||
b.WriteString(x.k)
|
||||
b.WriteString(",")
|
||||
b.WriteString(x.v)
|
||||
}
|
||||
key := b.String()
|
||||
if !members[key] {
|
||||
res = append(res, sub)
|
||||
members[key] = true
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// enumerateLeetSubs returns the list of possible 1337 replacement dictionaries for a given password
|
||||
func enumerateLeetSubs(table map[string][]string) []map[string]string {
|
||||
var keys []string
|
||||
for k := range table {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
var subs = [][]kv{[]kv{}}
|
||||
|
||||
var helper func(keys []string)
|
||||
helper = func(keys []string) {
|
||||
if len(keys) == 0 {
|
||||
return
|
||||
}
|
||||
firstKey := keys[0]
|
||||
restKeys := keys[1:]
|
||||
var nextSubs [][]kv
|
||||
for _, l33tChr := range table[firstKey] {
|
||||
for _, sub := range subs {
|
||||
dupL33tIndex := -1
|
||||
for i := 0; i < len(sub); i++ {
|
||||
if sub[i].k == l33tChr {
|
||||
dupL33tIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
if dupL33tIndex == -1 {
|
||||
subExtension := append(sub, kv{k: l33tChr, v: firstKey})
|
||||
nextSubs = append(nextSubs, subExtension)
|
||||
} else {
|
||||
subAlternative := make([]kv, 0, len(sub))
|
||||
subAlternative = append(subAlternative, sub[0:dupL33tIndex]...)
|
||||
subAlternative = append(subAlternative, sub[dupL33tIndex+1:]...)
|
||||
subAlternative = append(subAlternative, kv{k: l33tChr, v: firstKey})
|
||||
// subAlternative := make([]kv, 0, len(sub))
|
||||
// subAlternative = append(subAlternative, sub)
|
||||
// subAlternative[dupL33tIndex] = {k:l33tChr,v:firstKey}
|
||||
nextSubs = append(nextSubs, sub)
|
||||
nextSubs = append(nextSubs, subAlternative)
|
||||
}
|
||||
}
|
||||
}
|
||||
subs = dedup(nextSubs)
|
||||
helper(restKeys)
|
||||
}
|
||||
|
||||
helper(keys)
|
||||
var subDicts []map[string]string
|
||||
for _, sub := range subs {
|
||||
subDict := make(map[string]string)
|
||||
|
||||
for _, x := range sub {
|
||||
subDict[x.k] = x.v
|
||||
}
|
||||
subDicts = append(subDicts, subDict)
|
||||
}
|
||||
return subDicts
|
||||
}
|
||||
|
||||
func relevantSubtable(password string, table map[string][]string) map[string][]string {
|
||||
passwordChars := make(map[rune]bool)
|
||||
for _, chr := range password {
|
||||
passwordChars[chr] = true
|
||||
}
|
||||
|
||||
relevantSubs := make(map[string][]string)
|
||||
for key, values := range table {
|
||||
for _, value := range values {
|
||||
if passwordChars[rune(value[0])] {
|
||||
relevantSubs[key] = append(relevantSubs[key], value)
|
||||
}
|
||||
}
|
||||
}
|
||||
return relevantSubs
|
||||
}
|
||||
Reference in New Issue
Block a user