# makepass *This is a work in progress!* I wrote `makepass` long ago to just spit out some random strings I could use as passwords. The first versions has been lost to time, and `makepass` was first commited to a git-repo in 2018, as part of my dotfile-repo [idgatt](https://git.dnns.no/idgatt/) ("It's Dangerous to Go Alone, Take This!"). `makepass` was first written as a bash-script, but it was a zsh-script when I commited it to my dotfile-repo. It was later changed back to a bash-script, and in 2022 I rewrote it as both a POSIX shell-script and as a zsh-script. Around then I found some joy in tinkering with the script, and I did some optimalization of the zsh-script to minimilize forks and runtime. It is now pure zsh, and does not fork out to any other programs. Recently I decided I wanted to try to recreate makepass in other languages, and maybe see if I can make it even faster. ## makepass specifications Here's the synopsis from `makepass.zsh`, which explains how makepass should work: ``` NAME makepass - create several random passwords SYNOPSIS makepass [OPTIONS] [NUM] If a NUM is provided, passwords will be NUM characters long. By default `makepass` will output passwords from the three following classes: - Normal passwords - random strings with letters (both lower and upper case), numbers, and dashes and underscores. - Passwords with special characters - random strings generated from lower and upper case letters, numbers, and the following characters: !#$%&/()=?+-_,.;:<>[]{}|@* - Passphrases - if we find a dictionary, a series of eight random words from the dictionary, separated by dashes. The number of words can not be changed, but you do not have to use all of them. Use as mane as you want. The first and last letter will always be a letter. DESCRIPTION makepass has the following options: -h output this help-text -l length of passwords. See MAKEPASS_LENGTH below -n number of passwords. See MAKEPASS_NUMBER below -p print length of number ENVIRONMENT makepass examines the following environmental variables. MAKEPASS_LENGTH Specifies the length of passwords. Valid values are 0-255. If 0, a random value between 8 and 42 will be used for each password. -l overrides this environmental variable, and the argument NUM overrides that again. So `MAKEPASS_LENGTH=10 makepass -l 12 14` will give passwords that are 14 characters long, even though both -l and MAKEPASS_LENGTH also specifies a length. MAKEPASS_NUMBER The number of passwords to generate. This formula is used to determine how many passwords from each group should be generated: - (n) normal passwords - (n / 3 * 2 + 1) special passwords - (n / 2) passphrases Where n is 10 by default. Valid values for n are 1-255. Floating-poing math is not used, so results may vary. MAKEPASS_PRINTLEN If 1, print length of all passwords. If 0, don't. MAKEPASS_NORMAL String of characters from which to generate "normal" passwords. Defaults to: abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_ MAKEPASS_SPECIAL String of characters from which to generate passwords with special characters. Defaults to the same characters as in MAKEPASS_NORMAL, plus these: !#$%&/()=?+-_,.;:<>[]{}|@* MAKEPASS_WORDLIST Specifies the dictionary we find words for passphrases in. If this is unset or empty, we try "/usr/share/dict/words". If that file does not exist, no passphrases will be provided. NOTES This scripts makes use of $RANDOM - a builtin in zsh which produces a pseudo-random integer between 0 and 32767, newly generated each time the parameter is referenced. We initially seed the random number generator with a random 32bit integer generated from /dev/random. This should provide enough randomnes to generate sufficiently secure passwords. AUTHOR Dennis Eriksen ' ``` Also, the passwords should *preferably* be output in columns. The number of columns should be dynamic, and dependant on the width of the screen. ### Character sets Normal character set: `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_` Special character set: `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&/()=?+-_,.;:<>[]{}|\@*` ### Example output ``` Normal passwords: 7B39aQZSm9P9BYZ8gBu0rLNIjNP SLztHlkZ5IsG5VaMfeF_JgttZOfZZa5 RxnvFtdztaX jZ6lAAA2a38ip5gwr_LuIDOys2Co kn3hGdRMyIyz1sJic5iNL6N5 3Z9wWyScY4qqQYh wigDZcuGDlWDyW4UKNtC_NHg9ITVfLOUq4Iq4R dJJSK10_SDk7q6Jk 41uPXf5d-jYCh7wgc0ZOy hTTcPdqGg5NWCyf7vcFp3su4kGQ0aIyP7xK7WVcp Passwords with special characters: p0M4b1%XP.BsIDH1ub[q9b+3nSR84!W$W%W Passphrases: brisk-gong-gag-open-life-boil womb-nanny-stove-word-ajar-ocean sage-barge-barge-five-poise-coach lunar-juror-savor-unit-boil-sleep cloth-nap-word-shun-gulp-sedan guide-fray-dial-grid-candy-wick ``` ## Benchmarks I started out using just zshs `time` to time the runtime, but recently I started checking out [hyperfine](https://github.com/sharkdp/hyperfine) which does a nice job. Here are the results so far: ``` % hyperfine --time-unit=millisecond --warmup=1 --shell=none ./makepass.* Benchmark 1: ./makepass.bash Time (mean ± σ): 75.5 ms ± 2.1 ms [User: 32.1 ms, System: 38.2 ms] Range (min … max): 71.9 ms … 79.5 ms 39 runs Benchmark 2: ./makepass.go Time (mean ± σ): 6.9 ms ± 0.2 ms [User: 1.5 ms, System: 4.1 ms] Range (min … max): 6.4 ms … 8.2 ms 432 runs Benchmark 3: ./makepass.pl Time (mean ± σ): 38.6 ms ± 0.3 ms [User: 16.8 ms, System: 20.8 ms] Range (min … max): 38.0 ms … 39.4 ms 77 runs Benchmark 4: ./makepass.sh Time (mean ± σ): 1138.7 ms ± 15.4 ms [User: 268.0 ms, System: 1875.0 ms] Range (min … max): 1114.3 ms … 1157.5 ms 10 runs Benchmark 5: ./makepass.zsh Time (mean ± σ): 26.3 ms ± 0.7 ms [User: 12.1 ms, System: 11.6 ms] Range (min … max): 24.7 ms … 28.3 ms 112 runs Summary './makepass.go' ran 3.80 ± 0.16 times faster than './makepass.zsh' 5.57 ± 0.19 times faster than './makepass.pl' 10.89 ± 0.47 times faster than './makepass.bash' 164.37 ± 5.94 times faster than './makepass.sh' ``` ## Versions ### Bash This version needs a bit more work. ### Go Build width (from root folder in repo): ``` $ go build -o ../makepass.go -C go/ -ldflags "-s -w" makepass.go ``` ### Perl Perl version. I like this version. ### Shell Needs more work. ### Zsh This is currently the "main" version. It uses pure zsh, with no forking out to other programs. As of adding the go-version, it is the second fastest version.