Password generation may seem to be trival these days, as various password generators do exist. But I wanted to know if they are any good and I found two programs for the command line trying to examine the strength of a password:
-
cracklib-check
from the CrackLib project is a standalone program derived from the pam_cracklib module.
- An alternative to
pam_cracklib
is passwdqc from the Openwall Project which also provides standalone programs to generate and check passwords.
Let's use these two programs on a few password generators. For the sake of simplicity, let's only generate
random looking passwords that are exactly
12 characters long. Nobody should have to
type passwords these days anyway and a password manager is
recommended.
pwgen
pwgen (v2.06 from 2007) is maybe the most popular one, and easy to use too. The last version has been released in 2007, let's see if it is any good:
$ time pwgen -s -1 12 100000 | /usr/sbin/cracklib-check | fgrep -c -v ': OK'
20
real 0m31.722s
user 0m20.072s
sys 0m13.736s
→ We generated 100k passwords in 32 seconds, 20 of them (0.02%) did not pass the
cracklib
test.
$ time pwgen -s -1 12 100000 | pwqcheck -1 --multi | fgrep -c -v OK:
2731
real 2m42.557s
user 2m43.156s
sys 0m1.668s
→ We generated 100k passwords in 163 seconds, 2731 of them (2.7%) did not pass the
pwqcheck
test. Clearly,
pwqcheck
seems to be much stricter. It also takes much longer to check, but this should only matter when checking thousands of passwords, as we just did.
pwqgen
pwqgen
(v1.3.0 from 2013) from the
passwdqc project has a weird
syntax, probably due to the fact that it's mostly used as a
PAM module rather than as a standalone program. I couldn't figure out how to generate passwords of exactly 12 characters long, the following use of
cut(1)
will miss passwords
shorter than 12 characters:
$ seq 1 100000 | while read a; do pwqgen; done | cut -c-12 | ...
So, for the sake of correctness, let's do this instead (although this takes ~3 times longer to complete):
$ i=0; time while [ $i -lt 100000 ]; do pwqgen | cut -c-12 | egrep -o '^.{12}$' && i=$((i+1)); done \
| /usr/sbin/cracklib-check | fgrep -c -v ': OK'
9
real 6m4.756s
user 3m51.396s
sys 1m22.528s
→ We generated 100k passwords in 364 seconds, 9 of them (0.009%) did not pass the
cracklib
test. Clearly,
pwqgen
is generating much better passwords than
pwgen
, according to
cracklib
.
And again with
pwqcheck
:
$ i=0; time while [ $i -lt 100000 ]; do pwqgen | cut -c-12 | egrep -o '^.{12}$' && i=$((i+1)); done \
| pwqcheck -1 --multi | fgrep -c -v OK:
29083
real 6m16.043s
user 6m0.292s
sys 1m8.708s
→ We generated 100k passwords in 376 seconds, 29083 of them (29%) did not pass the
pwqcheck
test. Wow. This even contradicts the finding above: while
pwqgen
does seem to generate better passwords than
pwgen
according to
cracklib
, when checked with
pwqcheck
, password quality seems to be much lower. Let's attribute that to our
cut -c-12
hack and move on to another password generator:
apg
apg (v2.2.3 from 2003) hasn't had a release in 10 years and is kinda slow, since it's using
/dev/random directly, for
whatever reason:
$ time apg -a 1 -m 12 -x 12 -n 100000 | /usr/sbin/cracklib-check | fgrep -c -v ': OK'
67
real 4m28.997s
user 4m56.896s
sys 0m17.712s
→ We generated 100k passwords in 269 seconds, 67 of them (0.067%) did not pass the
cracklib
test.
And again with
pwqcheck
:
$ time apg -a 1 -m 12 -x 12 -n 100000 | pwqcheck -1 --multi | fgrep -c -v OK:
291
real 5m15.415s
user 9m30.960s
sys 0m0.420s
→ We generated 100k passwords in 315 seconds, 291 of them (0.29%) did not pass the
pwqcheck
test.
gpw
gpw (v0.0.19940601 from 2006) attempts to produce
pronounceable passwords, so our criteria for
random passwords won't hold. Let's test it anyway and see what happens:
$ time gpw 100000 12 | /usr/sbin/cracklib-check | fgrep -c -v ': OK'
540
real 0m28.195s
user 0m19.640s
sys 0m10.756s
→ We generated 100k passwords in 28 seconds, 540 of them (0.54%) did not pass the
cracklib
test.
$ time gpw 100000 12 | pwqcheck -1 --multi | fgrep -c -v OK:
100000
real 0m1.670s
user 0m1.768s
sys 0m0.016s
Wow - none of the passwords generated by
gpw
was accepted by
pwqcheck
! Execution time was very fast, though :-)
makepasswd
makepasswd (v1.10 from 2013) is a
Perl program, and a very fast one too:
$ time makepasswd --chars=12 --count=100000 | /usr/sbin/cracklib-check | fgrep -c -v ': OK'
22
real 0m34.404s
user 0m32.624s
sys 0m13.428s
→ We generated 100k passwords in 34 seconds, 22 of them (0.022%) did not pass the
cracklib
test.
$ time makepasswd --chars=12 --count=100000 | pwqcheck -1 --multi | fgrep -c -v OK:
12742
real 2m29.020s
user 2m40.328s
sys 0m0.024s
→ We generated 100k passwords in 149 seconds, 12742 of them (12.74%) did not pass the
pwqcheck
test.
So, in conclusion: use
pwqcheck
to check for passwords and
apg
or
pwgen
for password generation. To always use a password checker when generating a password, use something like this:
$ pwgen_check() { pwgen $@ | pwqcheck -1 --multi; }
$ pwgen_check -s 12 10
OK: CjgR1nC4t9t5
OK: iggW9u3hMAnd
OK: bMGGgqAm2WOB
OK: E7fAY7fjF5KJ
Bad passphrase (not enough different characters or classes for this length): FexzFoJRxpO5
OK: Dmz4VJBnUgQC
OK: JnIcezRq39SY
OK: EdaAK7gbBJDM
OK: TUmzflKP3npZ
OK: pSkPzf0fHnlw
While the "benchmarks" above were made up as I discovered more and more password generators, I wrote a
small script combining all these, generating the following results:
$ time ./password-test.sh 12 1000000
pwgen - 148 passwords (0%) failed for cracklib, runtime: 239 seconds.
pwqgen - 127 passwords (0%) failed for cracklib, runtime: 1605 seconds.
apg - 635 passwords (0%) failed for cracklib, runtime: 3225 seconds.
gpw - 5249 passwords (0%) failed for cracklib, runtime: 188 seconds.
makepasswd - 248 passwords (0%) failed for cracklib, runtime: 285 seconds.
openssl - 175 passwords (0%) failed for cracklib, runtime: 5509 seconds.
pwgen - 29523 passwords (2.00%) failed for pwqcheck, runtime: 1133 seconds.
pwqgen - 290042 passwords (29.00%) failed for pwqcheck, runtime: 2248 seconds.
apg - 3013 passwords (0%) failed for pwqcheck, runtime: 4082 seconds.
gpw - 1000000 passwords (100.00%) failed for pwqcheck, runtime: 21 seconds.
makepasswd - 128036 passwords (12.00%) failed for pwqcheck, runtime: 1029 seconds.
openssl - 100438 passwords (10.00%) failed for pwqcheck, runtime: 6417 seconds.
real 433m0.997s
user 352m16.577s
sys 120m52.305s