aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
blob: 1abd74cdd03aad3709b8efdc7a49458d38d07e00 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# 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-!<W
QJa%g3ufIJaPw3C;Yla[=peTQ,>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 <https://dnns.no>'
```

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!#$%&/()=?+-_,.;:<>[]{}|@*`

## 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 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.

### Rust
Rust-version. It's fast!

Build with (from root):

```
$ cargo build --manifest-path rust/Cargo.toml --release && mv rust/target/release/makepass makepass.rs
```

### 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.