Skip to content
This repository has been archived by the owner on Feb 1, 2023. It is now read-only.

changing l33t substitution set of possibilities #28

Merged
merged 1 commit into from
May 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
209 changes: 183 additions & 26 deletions matching/leet.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,7 @@ func FilterL33tMatcher(m match.Matcher) bool {
}

func l33tMatch(password string) []match.Match {

substitutions := relevantL33tSubtable(password)

permutations := getAllPermutationsOfLeetSubstitutions(password, substitutions)
permutations := getPermutations(password)

var matches []match.Match

Expand All @@ -35,41 +32,201 @@ func l33tMatch(password string) []match.Match {
return matches
}

func getAllPermutationsOfLeetSubstitutions(password string, substitutionsMap map[string][]string) []string {
// This function creates a list of permutations based on a fixed table stored on data. The table
// will be reduced in order to proceed in the function using only relevant values (see
// relevantL33tSubtable).
func getPermutations(password string) []string {
substitutions := relevantL33tSubtable(password)
permutations := getAllPermutationsOfLeetSubstitutions(password, substitutions)
return permutations
}

// This function loads the table from data but only keep in memory the values that are present
// inside the provided password.
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
}

// This function creates the list of permutations of a given password using the provided table as
// reference for its operation.
func getAllPermutationsOfLeetSubstitutions(password string, table map[string][]string) []string {
result := []string{}

// create a list of tables without conflicting keys/values (this happens for "|", "7" and "1")
noConflictsTables := createListOfMapsWithoutConflicts(table)
for _, noConflictsTable := range noConflictsTables {
substitutionsMaps := createSubstitutionsMapsFromTable(noConflictsTable)
for _, substitutionsMap := range substitutionsMaps {
newValue := createWordForSubstitutionMap(password, substitutionsMap)
if !stringSliceContainsValue(result, newValue) {
result = append(result, newValue)
}
}
}

var permutations []string
return result
}

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:]
// Create the possible list of maps removing the conflicts from it. As an example, the value "|"
// may represent "i" and "l". For each representation of the conflicting value, a new map is
// created. This may grow exponencialy according to the number of conflicts. The number of maps
// returned by this function may be reduced if the relevantL33tSubtable function was called to
// identify only relevant items.
func createListOfMapsWithoutConflicts(table map[string][]string) []map[string][]string {
// the resulting list starts with the provided table
result := []map[string][]string{}
result = append(result, table)

// iterate over the list of conflicts in order to expand the maps for each one
conflicts := retrieveConflictsListFromTable(table)
for _, value := range conflicts {
newMapList := []map[string][]string{}

// for each conflict a new list of maps will be created for every already known map
for _, currentMap := range result {
newMaps := createDifferentMapsForLeetChar(currentMap, value)
newMapList = append(newMapList, newMaps...)
}

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)
}
result = newMapList
}

}
return result
}

// This function retrieves the list of values that appear for one or more keys. This is usefull to
// know which l33t chars can represent more than one letter.
func retrieveConflictsListFromTable(table map[string][]string) []string {
result := []string{}
foundValues := []string{}

for _, values := range table {
for _, value := range values {
if stringSliceContainsValue(foundValues, value) {
// only add on results if it was not identified as conflict before
if !stringSliceContainsValue(result, value) {
result = append(result, value)
}
} else {
foundValues = append(foundValues, value)
}
}
}

return permutations
return result
}

func relevantL33tSubtable(password string) map[string][]string {
relevantSubs := make(map[string][]string)
for key, values := range L33T_TABLE.Graph {
// This function aims to create different maps for a given char if this char represents a conflict.
// If the specified char is not a conflit one, the same map will be returned. In scenarios which
// the provided char can not be found on map, an empty list will be returned. This function was
// designed to be used on conflicts situations.
func createDifferentMapsForLeetChar(table map[string][]string, leetChar string) []map[string][]string {
result := []map[string][]string{}

keysWithSameValue := retrieveListOfKeysWithSpecificValueFromTable(table, leetChar)
for _, key := range keysWithSameValue {
newMap := copyMapRemovingSameValueFromOtherKeys(table, key, leetChar)
result = append(result, newMap)
}

return result
}

// This function retrieves the list of keys that can be represented using the given value.
func retrieveListOfKeysWithSpecificValueFromTable(table map[string][]string, valueToFind string) []string {
result := []string{}

for key, values := range table {
for _, value := range values {
if strings.Contains(password, value) {
relevantSubs[key] = append(relevantSubs[key], value)
if value == valueToFind && !stringSliceContainsValue(result, key) {
result = append(result, key)
}
}
}
return relevantSubs

return result
}

// This function returns a lsit of substitution map from a given table. Each map in the result will
// provide only one representation for each value. As an example, if the provided map contains the
// values "@" and "4" in the possibilities to represent "a", two maps will be created where one
// will contain "a" mapping to "@" and the other one will provide "a" mapping to "4".
func createSubstitutionsMapsFromTable(table map[string][]string) []map[string]string {
result := []map[string]string{{"": ""}}

for key, values := range table {
newResult := []map[string]string{}

for _, mapInCurrentResult := range result {
for _, value := range values {
newMapForValue := copyMap(mapInCurrentResult)
newMapForValue[key] = value
newResult = append(newResult, newMapForValue)
}
}

result = newResult
}

// verification to make sure that the slice was filled
if len(result) == 1 && len(result[0]) == 1 && result[0][""] == "" {
return []map[string]string{}
}

return result
}

// This function replaces the values provided on substitution map over the provided word.
func createWordForSubstitutionMap(word string, substitutionMap map[string]string) string {
result := word
for key, value := range substitutionMap {
result = strings.Replace(result, value, key, -1)
}

return result
}

func stringSliceContainsValue(slice []string, value string) bool {
for _, valueInSlice := range slice {
if valueInSlice == value {
return true
}
}

return false
}

func copyMap(table map[string]string) map[string]string {
result := make(map[string]string)

for key, value := range table {
result[key] = value
}

return result
}

// This function creates a new map based on the one provided but excluding possible representations
// of the same value on other keys.
func copyMapRemovingSameValueFromOtherKeys(table map[string][]string, keyToFix string, valueToFix string) map[string][]string {
result := make(map[string][]string)

for key, values := range table {
for _, value := range values {
if !(value == valueToFix && key != keyToFix) {
result[key] = append(result[key], value)
}
}
}

return result
}
Loading