Xiong Chiamiov
Xiong Chiamiov

Reputation: 13744

Generate all possible n-character passwords

As part of a learning-Go exercise, I'm writing a simplistic brute-force password cracker.

To generate all possible 2-character passwords that use the characters A-E in Python, I would use itertools.product():

from itertools import product
for permutation in product('ABCDE', repeat=2):
  print permutation

However, I'm struggling to do this in Go.

Other questions seem to be about permutations, which isn't quite what I want. And while the Python docs include a sample implementation of the function, I don't know how to translate yield into Go.

I suppose I should mention two restrictions:

  1. I'd like the length of the password to be variable. That is, I may want to do 8-character passwords, or 6-character, or something else. This means we can't just nest n loops.
  2. I don't want to have all of them in memory at once.

Upvotes: 7

Views: 3666

Answers (5)

Mowshon
Mowshon

Reputation: 999

There is an Iterium package: https://github.com/mowshon/iterium

Here is how to generate all possible passwords using Iterium.Product() and Iterium.Map()

package main

import (
    "fmt"
    "github.com/mowshon/iterium"
    "strings"
)

func main() {
    letters := []string{"A", "B", "C", "D", "E"}
    product := iterium.Product(letters, 2)

    // Merge a character slice into a string type password
    //
    // Already has all the letters of the Latin alphabet in upper
    // and lower case `iterium.AsciiLetters`
    // https://github.com/mowshon/iterium/blob/main/string.go
    // You can use it and not have to create a slice from scratch.
    //
    // e.g. iterium.Product(iterium.AsciiLetters, 2)
    passwords := iterium.Map(product, func(slice []string) string {
        return strings.Join(slice, "")
    })

    fmt.Println("Total:", passwords.Count())
    for password := range passwords.Chan() {
        fmt.Println(password)
    }
}

Output:

Total: 25
AA
AB
AC
AD
AE
BA
BB
BC
BD
BE
CA
CB
CC
CD
CE
DA
DB
DC
DD
DE
EA
EB
EC
ED
EE

Upvotes: 0

Pradeep Jhuriya
Pradeep Jhuriya

Reputation: 51

package main

import  (
    "fmt"
    "strings"
    "strconv"

    // permutation and combination of charactersList
    "github.com/ernestosuarez/itertools"
)


func main()  {
    passwordLength := "1,2,4"
    characters := "abcdefghijklmnopqrstuvwxyz0123456789!@#$%^&*()+-./"

    passwordLengthList := strings.Split(passwordLength, ",")
    charactersList := strings.Split(characters, "")

    for _, passLen := range passwordLengthList {
    
        passLenInt, err := strconv.Atoi(passLen)
        if err != nil {
            panic(err)
        }

        for v := range itertools.PermutationsStr(charactersList, passLenInt) {
            fmt.Println(strings.Join(v, ""))
        }
    }
}

Upvotes: 1

Myrachanto
Myrachanto

Reputation: 11

uses select for channels to generate unique passwords

func randombitsGen(l int) (out chan string) {
Capschar := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
smallchar := "abcdefghijklmnopqrstuvwxyz"
nums := "0123456789"
specials := "!@#$%ˆ&*()?><"
out = make(chan string, l)
defer close(out)
for {
    select {
    case out <- string(Capschar[rand.Intn(len(strings.Split(Capschar, "")))]):
    case out <- string(Capschar[rand.Intn(len(strings.Split(Capschar, "")))]):
    case out <- string(Capschar[rand.Intn(len(strings.Split(Capschar, "")))]):
    case out <- string(smallchar[rand.Intn(len(strings.Split(smallchar, "")))]):
    case out <- string(smallchar[rand.Intn(len(strings.Split(smallchar, "")))]):
    case out <- string(smallchar[rand.Intn(len(strings.Split(smallchar, "")))]):
    case out <- string(nums[rand.Intn(len(strings.Split(nums, "")))]):
    case out <- string(specials[rand.Intn(len(strings.Split(specials, "")))]):
    default:
        return

    }
}

}

Upvotes: -1

peterSO
peterSO

Reputation: 166815

For example, satisfying your restrictions,

package main

import "fmt"

func nextPassword(n int, c string) func() string {
    r := []rune(c)
    p := make([]rune, n)
    x := make([]int, len(p))
    return func() string {
        p := p[:len(x)]
        for i, xi := range x {
            p[i] = r[xi]
        }
        for i := len(x) - 1; i >= 0; i-- {
            x[i]++
            if x[i] < len(r) {
                break
            }
            x[i] = 0
            if i <= 0 {
                x = x[0:0]
                break
            }
        }
        return string(p)
    }
}

func main() {
    np := nextPassword(2, "ABCDE")
    for {
        pwd := np()
        if len(pwd) == 0 {
            break
        }
        fmt.Println(pwd)
    }
}

Output:

AA
AB
AC
AD
AE
BA
BB
BC
BD
BE
CA
CB
CC
CD
CE
DA
DB
DC
DD
DE
EA
EB
EC
ED
EE

Upvotes: 8

Linear
Linear

Reputation: 22226

What you want is basically the n-ary cartesian product of a set with itself. So for all 3-character passwords you want Prod(set,set,set). This can be constructed iteratively. First construct the n-1 product, then for each product and each element of the initial set, add the element. So for instance all 2 character passwords -> 3 character passwords where the only valid characters are 'a' or 'b'.

"ab" = {a,b} -> {(a,a),(a,b),(b,a),(b,b)} -> {(a,a,a),(a,a,b),(a,b,a),(a,b,b),(b,a,a),(b,a,b),(b,b,a),(b,b,b)}

func NAryProduct(input string, n int) []string {
    if n <= 0 {
        return nil
    }

    // Copy input into initial product set -- a set of
    // one character sets
    prod := make([]string, len(input))
    for i, char := range input {
        prod[i] = string(char)
    }

    for i := 1; i < n; i++ {
        // The bigger product should be the size of the input times the size of
        // the n-1 size product
        next := make([]string, 0, len(input)*len(prod))

        // Add each char to each word and add it to the new set
        for _, word := range prod {
            for _, char := range input {
                next = append(next, word + string(char))
            }
        }

        prod = next
    }

    return prod
}

Playground version: http://play.golang.org/p/6LhApeJ1bv

It should be noted that there's a lot of room for improvement on this solution. If you want to construct all passwords of length, say, 6-18, calling this method independently for each one will recalculate previously computed sets. I'll leave writing the better version up to you. Given what I've shown you, it shouldn't be too difficult to modify the code to take an arbitrary (n-m)ary product and compute the n-ary product from it. (Hint: think about how you'd do this recursively)

Upvotes: 11

Related Questions