This commit is contained in:
Illya Marchenko 2024-03-28 13:17:50 +02:00
commit 8889eccef6
Signed by: stuzer05
GPG Key ID: A6ABAAA9268F9F4F
14 changed files with 326 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
/.idea
/passgen

View File

@ -0,0 +1,10 @@
package sets
func AsciiLowercase() (dict []int) {
// Print lowercase letters (ASCII 97 to 122)
for i := 97; i <= 122; i++ {
dict = append(dict, i)
}
return
}

View File

@ -0,0 +1,10 @@
package sets
func AsciiUppercase() (dict []int) {
// Print uppercase letters (ASCII 65 to 90)
for i := 65; i <= 90; i++ {
dict = append(dict, i)
}
return
}

View File

@ -0,0 +1,50 @@
package sets
func Chinese() (dict []int) {
// CJK Unified Ideographs (Common)
for i := 0x4E00; i <= 0x9FFF; i++ {
dict = append(dict, i)
}
// CJK Unified Ideographs Extension A (Rare)
for i := 0x3400; i <= 0x4DBF; i++ {
dict = append(dict, i)
}
// CJK Unified Ideographs Extension B (Rare, historic)
for i := 0x20000; i <= 0x2A6DF; i++ {
dict = append(dict, i)
}
// CJK Unified Ideographs Extension C (Rare, historic)
for i := 0x2A700; i <= 0x2B73F; i++ {
dict = append(dict, i)
}
// CJK Unified Ideographs Extension D (Uncommon, some in current use)
for i := 0x2B740; i <= 0x2B81F; i++ {
dict = append(dict, i)
}
// CJK Unified Ideographs Extension E (Rare, historic)
for i := 0x2B820; i <= 0x2CEAF; i++ {
dict = append(dict, i)
}
// CJK Unified Ideographs Extension F (Rare, historic)
for i := 0x2CEB0; i <= 0x2EBEF; i++ {
dict = append(dict, i)
}
// CJK Unified Ideographs Extension G (Rare, historic)
for i := 0x30000; i <= 0x3134F; i++ {
dict = append(dict, i)
}
// CJK Unified Ideographs Extension H (Rare, historic)
for i := 0x31350; i <= 0x323AF; i++ {
dict = append(dict, i)
}
return
}

View File

@ -0,0 +1,37 @@
package sets
func Cyrillic() (dict []int) {
// Cyrillic: U+0400U+04FF (256 characters)
for i := 0x0400; i <= 0x04FF; i++ {
dict = append(dict, i)
}
// Cyrillic Supplement: U+0500U+052F (48 characters)
for i := 0x0500; i <= 0x052F; i++ {
dict = append(dict, i)
}
// Cyrillic Extended-A: U+2DE0U+2DFF (32 characters)
for i := 0x2DE0; i <= 0x2DFF; i++ {
dict = append(dict, i)
}
// Cyrillic Extended-B: U+A640U+A69F (96 characters)
for i := 0xA640; i <= 0xA69F; i++ {
dict = append(dict, i)
}
// Cyrillic Extended-C: U+1C80U+1C8F (9 characters)
for i := 0x1C80; i <= 0x1C8F; i++ {
dict = append(dict, i)
}
// Cyrillic Extended-D: U+1E030U+1E08F (63 characters)
for i := 0x1E030; i <= 0x1E08F; i++ {
dict = append(dict, i)
}
dict = append(dict, []int{0x1D2B, 0x1D78}...)
return
}

View File

@ -0,0 +1,30 @@
package sets
func Japanese() (dict []int) {
// Japanese-style punctuation
for i := 0x3000; i <= 0x303F; i++ {
dict = append(dict, i)
}
// Hiragana
for i := 0x3040; i <= 0x309F; i++ {
dict = append(dict, i)
}
// Katakana
for i := 0x30A0; i <= 0x30FF; i++ {
dict = append(dict, i)
}
// Full-width roman characters and half-width katakana
for i := 0xFF00; i <= 0xFFEF; i++ {
dict = append(dict, i)
}
// CJK unified ideographs (common and uncommon kanji)
for i := 0x4E00; i <= 0x9FAF; i++ {
dict = append(dict, i)
}
return
}

View File

@ -0,0 +1,10 @@
package sets
func Numbers() (dict []int) {
// Print numbers (ASCII 48 to 57)
for i := 48; i <= 57; i++ {
dict = append(dict, i)
}
return
}

7
dictionary/sets/space.go Normal file
View File

@ -0,0 +1,7 @@
package sets
func Space() (dict []int) {
dict = append(dict, ' ')
return
}

View File

@ -0,0 +1,10 @@
package sets
func SpecialChars() (dict []int) {
// Print special symbols (ASCII 33 to 47)
for i := 33; i <= 47; i++ {
dict = append(dict, i)
}
return
}

14
dictionary/shuffle.go Normal file
View File

@ -0,0 +1,14 @@
package dictionary
import (
"math/rand/v2"
)
func ShuffleDictionarySet(slice []int) []int {
for i := len(slice) - 1; i > 0; i-- {
j := rand.IntN(i + 1)
slice[i], slice[j] = slice[j], slice[i]
}
return slice
}

48
generator.go Normal file
View File

@ -0,0 +1,48 @@
package main
import "math/rand/v2"
func generatePassword(sets [][]int, passwordLength int) []int {
numSets := len(sets)
totalLength := 0
for _, set := range sets {
totalLength += len(set)
}
props := make([]float64, numSets)
for i := range props {
props[i] = float64(len(sets[i])) / float64(totalLength)
}
numChars := make([]int, numSets)
for i := range numChars {
numChars[i] = int(props[i] * float64(passwordLength))
if numChars[i] == 0 {
numChars[i] = 1
}
}
totalChars := 0
for _, n := range numChars {
totalChars += n
}
if totalChars < passwordLength {
numChars[0]++
} else if totalChars > passwordLength {
numChars[numSets-1]--
}
password := make([]int, 0, passwordLength)
for i, set := range sets {
for j := 0; j < numChars[i]; j++ {
password = append(password, set[rand.IntN(len(set))])
}
}
rand.Shuffle(len(password), func(i, j int) {
password[i], password[j] = password[j], password[i]
})
return password
}

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module gitea.stuzer.link/stuzer05/passgen
go 1.20
require golang.org/x/text v0.9.0 // indirect

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=

91
main.go Normal file
View File

@ -0,0 +1,91 @@
package main
import (
"flag"
"fmt"
"gitea.stuzer.link/stuzer05/passgen/dictionary"
"gitea.stuzer.link/stuzer05/passgen/dictionary/sets"
)
func main() {
// password flags
flagLen := flag.Int("L", 16, "length of the generated password")
flagCount := flag.Int("c", 1, "how many passwords to generate")
// set flags
flagSetAll := flag.Bool("all", false, "use all character sets")
flagSetAsciiLowercase := flag.Bool("l", false, "use lowercase ascii characters")
flagSetAsciiUppercase := flag.Bool("U", false, "use uppercase ascii characters")
flagSetNumbers := flag.Bool("n", false, "use numbers")
flagSetSpecialChars := flag.Bool("s", false, "use special characters")
flagSetSpaces := flag.Bool("S", false, "use spaces")
flagSetUnicodeJapanese := flag.Bool("unicode-japanese", false, "use unicode Japanese characters")
flagSetUnicodeChinese := flag.Bool("unicode-chinese", false, "use unicode Chinese characters")
flag.Parse()
// check password length
if *flagLen <= 0 {
fmt.Println("password length must be at least 1")
}
// check password count
if *flagCount <= 0 {
fmt.Println("passwords count must be at least 1")
}
// use all character sets
if *flagSetAll {
*flagSetAsciiLowercase = true
*flagSetAsciiUppercase = true
*flagSetNumbers = true
*flagSetSpecialChars = true
*flagSetSpaces = true
*flagSetUnicodeJapanese = true
*flagSetUnicodeChinese = true
}
// use default password preset no sets were requested
if !*flagSetAll && !*flagSetAsciiLowercase && !*flagSetAsciiUppercase && !*flagSetNumbers && !*flagSetSpecialChars && !*flagSetSpaces && !*flagSetUnicodeJapanese && !*flagSetUnicodeChinese {
*flagSetAsciiLowercase = true
*flagSetAsciiUppercase = true
*flagSetNumbers = true
}
// select dictionaries
var characterSets [][]int
if *flagSetAsciiLowercase {
characterSets = append(characterSets, dictionary.ShuffleDictionarySet(sets.AsciiLowercase()))
}
if *flagSetAsciiUppercase {
characterSets = append(characterSets, dictionary.ShuffleDictionarySet(sets.AsciiUppercase()))
}
if *flagSetNumbers {
characterSets = append(characterSets, dictionary.ShuffleDictionarySet(sets.Numbers()))
}
if *flagSetSpecialChars {
characterSets = append(characterSets, dictionary.ShuffleDictionarySet(sets.SpecialChars()))
}
if *flagSetSpaces {
characterSets = append(characterSets, dictionary.ShuffleDictionarySet(sets.Space()))
}
// unicode
if *flagSetUnicodeJapanese {
characterSets = append(characterSets, dictionary.ShuffleDictionarySet(sets.Japanese()))
}
if *flagSetUnicodeChinese {
characterSets = append(characterSets, dictionary.ShuffleDictionarySet(sets.Chinese()))
}
// generate passwords
for i := 0; i < *flagCount; i++ {
password := generatePassword(characterSets, *flagLen)
for _, i := range password {
fmt.Printf("%c", i)
}
}
}