# makepass *This is a work in progress!* makepass is a program that outputs a series of random passwords with both "normal" and "special" characters, and a series of passphrases from a given dictionary. See below for an example of what the output looks like. makepass is implemented in several languages for both fun and learning. The current main version is `makepass.zsh`. The symlink `./makepass` will always point to the main version. ## History I wrote makepass long ago (somewhere between 2010 and 2013 I believe) 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. ## Example output ``` Normal passwords: ZmvQB4r23KnctqjoV8fAlObMNu9Yt f-FS1k7yXwxeTIZk IpCL6RiHgh0DsJGzE_mvQB4r23KnctqjoV8fAlY ANu9Yda5-FS1k7yXwxeTIZWPUpCL6RiHgh0Dsr vE_mvQB4r23KnctqjoV8fAlObMNu9YdaD dS1k7yXwxeTIZu wpCL6RiHgh0DsJGzE_mvQB4r23KnctqjoV8fAN XMNu9Yda5-FS1k7yXwxeTIZWPUpCL6 uHgh0DsJGzE_mV N4r23KnctqjoV8fAlObMNu9Yda5-FS1k7yXwxy Passwords with special characters: L&ROH=NKTcti-U#Sv_/Ojgx}1kd+jEFe)cX fwVCfYRWXghK9AR$-Ex.vktyrE5 HAZG-EhuL&t4H:x}vc74HwFe|&Raj@Fy w7>j!hev_JabEV(vIBqHMx.TcZGjwV0|c,i-!5E?}vAR4zMpCD6v PENeD&lyr@xKF ZazwVCLAJWzM?8fs{W%g#KvYZq[Mp(P Passphrases: tweak-boots-shun-pond-cone-wimp-happy-polar shut-acre-decay-thank-pagan-crown-snack-cadet slug-putt-twist-fresh-park-flock-deed-error music-rerun-mocha-acid-taps-boast-rule-clad churn-curvy-wreck-list-stove-bonus-ozone-atom ``` ## 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. #### Dependencies I want these programs to be able to run without having to install lots of extra packages. Therefore, I try to minimize external dependencies to what's available with POSIX. However, this limitation does not apply to the languages which get compiled - as long as they handle their own dependencies, and the binary can run without the need to install extra fluff. ### Character sets Normal character set: `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_` Special character set: `abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!#$%&/()=?+-_,.;:<>[]{}|@*` ## 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 ± σ): 81.2 ms ± 1.9 ms [User: 34.0 ms, System: 39.7 ms] Range (min … max): 77.3 ms … 85.2 ms 35 runs Benchmark 2: ./makepass.go Time (mean ± σ): 6.7 ms ± 0.1 ms [User: 1.0 ms, System: 4.9 ms] Range (min … max): 6.4 ms … 7.8 ms 444 runs Benchmark 3: ./makepass.pl Time (mean ± σ): 40.4 ms ± 0.3 ms [User: 19.7 ms, System: 18.3 ms] Range (min … max): 39.7 ms … 41.3 ms 75 runs Benchmark 4: ./makepass.py Time (mean ± σ): 103.5 ms ± 1.2 ms [User: 71.0 ms, System: 27.9 ms] Range (min … max): 101.9 ms … 107.8 ms 29 runs Benchmark 5: ./makepass.rs Time (mean ± σ): 4.9 ms ± 0.2 ms [User: 1.1 ms, System: 2.4 ms] Range (min … max): 4.4 ms … 5.6 ms 610 runs Benchmark 6: ./makepass.sh Time (mean ± σ): 638.4 ms ± 2.9 ms [User: 155.0 ms, System: 915.0 ms] Range (min … max): 634.0 ms … 643.9 ms 10 runs Benchmark 7: ./makepass.zsh Time (mean ± σ): 26.9 ms ± 0.7 ms [User: 13.4 ms, System: 12.0 ms] Range (min … max): 25.5 ms … 30.2 ms 109 runs Summary ./makepass.rs ran 1.36 ± 0.07 times faster than ./makepass.go 5.44 ± 0.31 times faster than ./makepass.zsh 8.19 ± 0.42 times faster than ./makepass.pl 16.46 ± 0.91 times faster than ./makepass.bash 20.96 ± 1.08 times faster than ./makepass.py 129.36 ± 6.50 times faster than ./makepass.sh ``` ## Versions ### Bash Bash-version. It's a lot messier than the zsh-version. ### Go Build width (from root folder in repo): ``` $ go build -C src/go -o .. -ldflags "-s -w" ``` ### Perl Perl version. I like this version <3 ### Python I don't really care for python, but here it is. It's pretty slow, but I don't really know Python. It should probably be faster. To lint and format with `ruff`, `ruff` must first be installed. ``` $ cd src/python $ python3 -m venv venv $ source venv/bin/activate $ pip install --upgrade pip $ pip install ruff $ ruff check makepass.py $ ruff format makepass.py ``` ### Rust Rust-version. It's fast! Build with (from root): ``` $ cargo build --manifest-path src/rust/Cargo.toml --release ``` ### Shell *Should* be pure POSIX sh. Needs some cleanup. ### Zsh This is currently the "main" version. It uses pure zsh, with no forking out to other programs. Aside from the compiled languages, it is by far the fastest version.