Skip to content

Instantly share code, notes, and snippets.

@CAFxX
Last active May 1, 2025 12:58
Show Gist options
  • Save CAFxX/ea71cdd65d7119c577a4d5cf2914b31a to your computer and use it in GitHub Desktop.
Save CAFxX/ea71cdd65d7119c577a4d5cf2914b31a to your computer and use it in GitHub Desktop.
pwgen
// https://go.dev/play/p/374e3zUuZmE
package main
import (
"crypto/rand"
"flag"
"fmt"
"maps"
"math/big"
"os"
"slices"
"github.com/CAFxX/xbig"
)
func main() {
npwd := flag.Int("n", 1, "Number of passwords to generate")
nchars := flag.Int("c", 0, "Number of characters to generate")
nbits := flag.Int("b", 0, "Number of bits of random data")
cset := flag.String("s", "alnum", "Character set")
flag.Parse()
if *npwd < 1 {
panic("illegal n")
}
var cs []byte
switch *cset {
case "base32":
cs = ranges('A', 'Z', '2', '7')
case "base64":
cs = ranges('a', 'z', 'A', 'Z', '0', '9', '+', '+', '/', '/')
case "print":
cs = ranges(32, 126)
case "alnum":
cs = ranges('a', 'z', 'A', 'Z', '0', '9')
case "lcalnum":
cs = ranges('a', 'z', '0', '9')
case "ucalnum", "base36":
cs = ranges('A', 'Z', '0', '9')
case "lchex", "hex":
cs = ranges('a', 'f', '0', '9')
case "uchex":
cs = ranges('A', 'F', '0', '9')
case "all":
cs = ranges(0, 255)
default:
fmt.Println("unknown character set")
os.Exit(-1)
}
if *nbits == 0 && *nchars == 0 {
*nbits = 128
} else if *nbits != 0 && *nchars != 0 {
fmt.Println("-b and -c can not be used together")
os.Exit(-1)
}
if len(cs) < 2 {
panic("cs too small")
} else if len(cs) > 255 {
panic("cs too big")
}
if *nbits != 0 {
_nchars, _ := xbig.QuoFloat(xbig.LogFloat(xbig.PowFloat(2, *nbits)), xbig.LogFloat(len(cs))).SetMode(big.ToPositiveInf).Int64()
*nchars = int(_nchars)
}
fmt.Fprintf(os.Stderr, "Generating %d password(s) nbits>=%d, nchars=%d, cs=%s, ncs=%d\n", *npwd, *nbits, *nchars, *cset, len(cs))
for j := 0; j < *npwd; j++ {
rd, err := rand.Int(rand.Reader, xbig.ExpInt(len(cs), *nchars))
if err != nil {
panic(err)
}
pwd := []byte{}
for i := 0; i < *nchars; i++ {
c := xbig.ModInt(rd, len(cs)).Int64()
rd = xbig.DivInt(rd, len(cs))
pwd = append(pwd, cs[c])
}
pwd = append(pwd, '\n')
os.Stdout.Write(pwd)
}
}
func ranges(r ...byte) []byte {
if len(r)%2 == 1 {
panic("must be pairs")
}
m := map[byte]struct{}{}
for i := 0; i < len(r); i += 2 {
for j := r[i]; j <= r[i+1]; j++ {
m[j] = struct{}{}
}
}
return slices.Collect(maps.Keys(m))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment