Skip to content

Commit

Permalink
Redo passphrase transformation
Browse files Browse the repository at this point in the history
- overhauled the passphrase transformation mode to be simpler and cover more hard to hit patterns
  • Loading branch information
JakeWnuk committed Sep 19, 2024
1 parent 02c0eba commit db62f53
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 89 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ git clone https://github.com/JakeWnuk/ptt && cd ptt && docker build -t ptt . &&

### Usage:
```
Usage of Password Transformation Tool (ptt) version (0.3.3):
Usage of Password Transformation Tool (ptt) version (0.3.4):
ptt [options] [...]
Accepts standard input and/or additonal arguments.
Expand Down Expand Up @@ -112,8 +112,8 @@ These create or alter based on the selected mode.
Transforms input by creating masks that still retain strings from file.
-t mask-swap -tf [file]
Transforms input by swapping tokens from a partial mask file and a input file.
-t passphrase -w [words] -tf [file]
Transforms input by randomly generating passphrases with a given number of words and separators from a file.
-t passphrase -w [words]
Transforms input by generating passphrases from sentences with a given number of words.
-t regram -w [words]
Transforms input by 'regramming' sentences into new n-grams with a given number of words.
-t replace-all -tf [file]
Expand Down
25 changes: 10 additions & 15 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,8 @@ The following transformations can be used with the `-t` flag:
Transforms input by creating masks that still retain strings from file.
-t mask-swap -tf [file]
Transforms input by swapping tokens from a partial mask file and a input file.
-t passphrase -w [words] -tf [file]
Transforms input by randomly generating passphrases with a given number of words and separators from a file.
-t passphrase -w [words]
Transforms input by generating passphrases from sentences with a given number of words.
-t regram -w [words]
Transforms input by 'regramming' sentences into new n-grams with a given number of words.
-t replace-all -tf [file]
Expand Down Expand Up @@ -525,8 +525,8 @@ wordlists. There are several ways to generate wordlists using PTT:
This is implemented in the `pop` module.
- `Token Swapping`: Generates tokens by swapping characters in a string. This is
implemented in the `mask-swap` module.
- `Passphrases`: Generates passphrases by combining words from a wordlist. This
is implemented in the `passphrase` module.
- `Passphrases`: Generates passphrases by reforming setences. This is implemented
in the `passphrase` module.

All modes support multibyte characters and can properly convert them. One
transformation can be used at a time.
Expand Down Expand Up @@ -593,19 +593,14 @@ candidates by using masks. However, it is unique in that it uses partial
masks to limit the swap positions from prior applications.

### Passphrases
The `passphrase` module generates passphrases by combining words from a wordlist.
The `-w` flag can be used to specify the number of words to use in the passphrase.
The `-tf` flag is optional and can be used to specify a file containing separators
to use between words. The syntax is as follows:
The `passphrase` module generates passphrases by reforming sentences. The syntax is as follows:
```
ptt -f <input-file> -t passphrase -w <word-count> -tf <separator-file>
ptt -f <input-file> -t passphrase -w <word-count>
```

The passphrases are generated randomly by selecting words and separators from the input.
If no separator file is provided, no separators will be used. The default word count is 0.
The number of passphrases generated is equal to the number of lines in the input file
*including* duplicates. This means that the item count is also used to determine the number
of passphrases generated.
The `passphrase` mode will generate new passphrases from the input by
reformatting the sentences into new passphrases. The number of words to use in
the passphrase is specified by the `-w` flag. The output will be the new
passphrases generated from the input with the specified word count.

## Misc Creation Guide

Expand Down
5 changes: 4 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@ module github.com/jakewnuk/ptt

go 1.23.0

require golang.org/x/net v0.24.0
require (
golang.org/x/net v0.24.0
golang.org/x/text v0.14.0
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
4 changes: 2 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
"github.com/jakewnuk/ptt/pkg/utils"
)

var version = "0.3.3"
var version = "0.3.4"
var wg sync.WaitGroup
var mutex = &sync.Mutex{}
var retain models.FileArgumentFlag
Expand Down Expand Up @@ -62,7 +62,7 @@ func main() {
"mask-match -tf [file]": "Transforms input by keeping only strings with matching masks from a mask file.",
"swap-single -tf [file]": "Transforms input by swapping tokens once per string per replacement with exact matches from a ':' separated file.",
"mask-swap -tf [file]": "Transforms input by swapping tokens from a partial mask file and a input file.",
"passphrase -w [words] -tf [file]": "Transforms input by randomly generating passphrases with a given number of words and separators from a file.",
"passphrase -w [words]": "Transforms input by generating passphrases from sentences with a given number of words.",
"substring -i [index]": "Transforms input by extracting substrings starting at index and ending at index.",
"replace-all -tf [file]": "Transforms input by replacing all strings with all matches from a ':' separated file.",
"regram -w [words]": "Transforms input by 'regramming' sentences into new n-grams with a given number of words.",
Expand Down
76 changes: 8 additions & 68 deletions pkg/transform/transform.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package transform

import (
"fmt"
"math/rand"
"os"
"strings"

Expand Down Expand Up @@ -121,7 +120,7 @@ func TransformationController(input map[string]int, mode string, startingIndex i
fmt.Fprintf(os.Stderr, "[!] Passphrase operations require use of the -w flag to specify the number of words to use\n")
os.Exit(1)
}
output = MakePassphraseMap(input, transformationFilesMap, bypass, functionDebug, passphraseWords)
output = MakePassphraseMap(input, bypass, functionDebug, passphraseWords)
case "substring":
output = utils.SubstringMap(input, startingIndex, endingIndex, bypass, functionDebug)
case "replace-all", "replace":
Expand Down Expand Up @@ -223,15 +222,11 @@ func ReplaceAllKeysInMap(originalMap map[string]int, replacements map[string]int
}

// MakePassphraseMap takes a map of keys and creates a new map with new
// passphrases for each key. The transformation file is used to insert
// separators between the words. If the replacement mask is set to blank, then
// the words are concatenated together without any separators. Passphrases are
// generated by selecting a random word from the transformation file for each key.
// passphrases for each key.
//
// Args:
//
// input (map[string]int): The original map to replace keys in
// transformationFilesMap (map[string]int): A map of transformation files to
// use for constructing the passphrases
// bypass (bool): If true, the map is not used for output or filtering
// debug (bool): If true, print additional debug information to stderr
Expand All @@ -240,82 +235,27 @@ func ReplaceAllKeysInMap(originalMap map[string]int, replacements map[string]int
// Returns:
//
// (map[string]int): A new map with the keys replaced
func MakePassphraseMap(input map[string]int, transformationFilesMap map[string]int, bypass bool, debug bool, passphraseWord int) map[string]int {
func MakePassphraseMap(input map[string]int, bypass bool, debug bool, passphraseWord int) map[string]int {
newMap := make(map[string]int)

for key, value := range input {
newKeyArray := utils.GeneratePassphrase(key, passphraseWord)
for _, newKey := range newKeyArray {

for i := 0; i < value; i++ {
newKeyPhrase := GeneratePassphrase(input, transformationFilesMap, passphraseWord)
if debug {
fmt.Fprintf(os.Stderr, "Key: %s\n", key)
fmt.Fprintf(os.Stderr, "New Phrase: %s\n", newKeyPhrase)
fmt.Fprintf(os.Stderr, "New Key: %s\n", newKey)
}

if !bypass {
newMap[newKeyPhrase] = value
newMap[newKey] = value
} else {
fmt.Println(newKeyPhrase)
fmt.Println(newKey)
}
}
}
return newMap
}

// GeneratePassphrase takes a key and a map of transformation files and
// generates a passphrase based on the number of words specified. The words
// are selected from the transformation files and concatenated together with
// a separator. If the replacement mask is set to blank, then the words are
// concatenated together without any separators.
//
// Args:
//
// passWords (map[string]int): Content of the passphrase for use as words in
// the passphrase
// transformationFilesMap (map[string]int): Content of the transformation
// files for use as separators between words
// passphraseWord (int): The number of words to use for passphrase generation
//
// Returns:
//
// (string): The generated passphrase
func GeneratePassphrase(passWords map[string]int, transformationFilesMap map[string]int, passphraseWord int) string {
words := make([]string, passphraseWord)

seps := make([]string, 0, len(transformationFilesMap))
for k := range transformationFilesMap {
seps = append(seps, k)
}

if len(seps) == 0 {
seps = append(seps, "")
}

keys := make([]string, 0, len(passWords))
for k := range passWords {
keys = append(keys, k)
}

for i := 0; i < passphraseWord; i++ {
sep := seps[rand.Intn(len(seps))]
key := keys[rand.Intn(len(keys))]

if i+1 >= passphraseWord {
words[i] = fmt.Sprintf("%s%s", key, "")
} else {
words[i] = fmt.Sprintf("%s%s", key, sep)
}

}

var newKeyPhrase string
for _, word := range words {
newKeyPhrase += word
}

return newKeyPhrase
}

// GenerateNGramMap takes a map of keys and values and generates a new map
// using the utils.GenerateNGrams function and combines the results. This
// function is used to generate n-grams from the input map for the regram
Expand Down
62 changes: 62 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"github.com/jakewnuk/ptt/pkg/models"

"golang.org/x/net/html"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -978,6 +980,66 @@ func GenerateNGrams(text string, n int) []string {
return nGrams
}

func GeneratePassphrase(text string, n int) []string {
words := strings.Fields(text)
var passphrases []string

if len(words) != n {
return passphrases
}

var titleCaseWords string
var turkTitleCaseWords string
var CAPSlowerWords []string
var lowerCAPSWords []string
var lowerl33tWords []string
var l33tlowerWords []string
var CAPSl33tWords []string
var l33tCAPSWords []string
tick := false
titleCaseWords = cases.Title(language.Und, cases.NoLower).String(text)
turkTitleCaseWords = cases.Upper(language.Turkish, cases.NoLower).String(text)

for _, word := range words {

if tick {
CAPSlowerWords = append(CAPSlowerWords, strings.ToUpper(word))
lowerCAPSWords = append(lowerCAPSWords, strings.ToLower(word))
lowerl33tWords = append(lowerl33tWords, strings.ToLower(word))
l33tlowerWords = append(l33tlowerWords, strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(word, "a", "4"), "e", "3")))
CAPSl33tWords = append(CAPSl33tWords, strings.ToUpper(word))
l33tCAPSWords = append(l33tCAPSWords, strings.ReplaceAll(strings.ReplaceAll(word, "a", "4"), "e", "3"))
} else {
CAPSlowerWords = append(CAPSlowerWords, strings.ToLower(word))
lowerCAPSWords = append(lowerCAPSWords, strings.ToUpper(word))
lowerl33tWords = append(lowerl33tWords, strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(word, "a", "4"), "e", "3")))
l33tlowerWords = append(l33tlowerWords, strings.ToLower(word))
CAPSl33tWords = append(CAPSl33tWords, strings.ReplaceAll(strings.ReplaceAll(word, "a", "4"), "e", "3"))
l33tCAPSWords = append(l33tCAPSWords, strings.ToUpper(word))
}

tick = !tick

}
CAPSlowerPassphrase := strings.Join(CAPSlowerWords, "")
lowerCAPSPassphrase := strings.Join(lowerCAPSWords, "")
lowerl33tPassphrase := strings.Join(lowerl33tWords, "")
l33tlowerPassphrase := strings.Join(l33tlowerWords, "")
CAPSl33tPassphrase := strings.Join(CAPSl33tWords, "")
l33tCAPSPassphrase := strings.Join(l33tCAPSWords, "")

passphrases = append(passphrases, strings.ReplaceAll(titleCaseWords, " ", ""))
passphrases = append(passphrases, strings.ReplaceAll(turkTitleCaseWords, " ", ""))
passphrases = append(passphrases, CAPSlowerPassphrase)
passphrases = append(passphrases, lowerCAPSPassphrase)
passphrases = append(passphrases, lowerl33tPassphrase)
passphrases = append(passphrases, l33tlowerPassphrase)
passphrases = append(passphrases, CAPSl33tPassphrase)
passphrases = append(passphrases, l33tCAPSPassphrase)

return passphrases
}

// ----------------------------------------------------------------------------
// Validation Functions
// ----------------------------------------------------------------------------
Expand Down
31 changes: 31 additions & 0 deletions pkg/utils/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
// - ReplaceAllSubstring()
// - SubstringMap()
// - GenerateNGrams()
// - GeneratePassphrase()
//
// ** Validation Functions **
// - CheckASCIIString()
Expand Down Expand Up @@ -659,6 +660,36 @@ func TestGenerateNGrams(t *testing.T) {
}
}

// Unit Test for GeneratePassphrase()
func TestGeneratePassphrase(t *testing.T) {

// Define a test case struct
type TestCase struct {
Input1 string
Input2 int
Output []string
}

type TestCases []TestCase

// Define test cases
testCases := TestCases{
{"I <3 you", 3, []string{"I<3you", "I<3YOU", "i<3you", "I<3YOU", "I<3You", "i<3you", "i<3you", "I<3YOU"}},
}

// Run test cases
for _, testCase := range testCases {
input1 := testCase.Input1
input2 := testCase.Input2
output := testCase.Output

given := GeneratePassphrase(input1, input2)
if CheckAreArraysEqual(given, output) == false {
t.Errorf("GeneratePassphrase(%v, %v) = %v; want %v", input1, input2, given, output)
}
}
}

// Unit Test for IsValidURL()
func TestIsValidURL(t *testing.T) {

Expand Down

0 comments on commit db62f53

Please sign in to comment.