summaryrefslogtreecommitdiff
path: root/mkpasswd
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2013-03-16 10:37:40 -0700
committerJesse Morgan <jesse@jesterpm.net>2013-03-16 10:37:40 -0700
commit0593e4279ac6a11c1b95c0cca35f20f631e1ff5c (patch)
tree06cbfd24ee8efed98881d16ace34b6a7c8c1da3e /mkpasswd
parentac5044074e01f518ae3c837dfad0fa3fc0719962 (diff)
Adding files from bismuth
Diffstat (limited to 'mkpasswd')
-rwxr-xr-xmkpasswd165
1 files changed, 165 insertions, 0 deletions
diff --git a/mkpasswd b/mkpasswd
new file mode 100755
index 0000000..3d681b0
--- /dev/null
+++ b/mkpasswd
@@ -0,0 +1,165 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+"""Generate a random, memorizable password: http://xkcd.com/936/
+
+Example run:
+
+ kragen at inexorable:~/devel/inexorable-misc$ ./mkpass.py 5 12
+ Your password is "learned damage saved residential stages".
+ That's equivalent to a 60-bit key.
+
+ That password would take 2.5e+03 CPU-years to crack
+ on my inexpensive Celeron E1200 from 2008,
+ assuming an offline attack on a MS-Cache hash,
+ which is the worst password hashing algorithm in common use,
+ slightly worse than even simple MD5.
+
+ The most common password-hashing algorithm these days is FreeBSD’s
+ iterated MD5; cracking such a hash would take 5.2e+06 CPU-years.
+
+ But a modern GPU can crack about 250 times as fast,
+ so that same iterated MD5 would fall in 2e+04 GPU-years.
+
+ That GPU costs about US$1.45 per day to run in 2011,
+ so cracking the password would cost about US$3e+09.
+
+I've started using a password generated this way in place of a 9-printable-
+ASCII-character random password, which is equally strong. Munroe's assertion
+that these passwords are much easier to memorize is correct. However, there is
+still a problem: because there are many fewer bits of entropy per character
+(about 1.7 instead of 6.6) there is a lot of redundancy in the password, and so
+attacks such as the ssh timing-channel attack (the Song, Wagner, and Tian
+Herbivore attack, which I learned about from Bram Cohen in the Bagdad Café in
+the wee hours one morning, years ago) and keyboard audio recording attacks have
+a much better chance of capturing enough information to make the password
+attackable.
+
+My countermeasure to the Herbivore attack, which works well with 9-character
+password but is extremely annoying with my new password, is to type the
+password with a half-second delay between characters, so that the timing
+channel does not carry much information about the actual characters used.
+Additionally, the lower length of the 9-character password inherently gives the
+Herbivore approach much less information to chew on.
+
+Other possible countermeasures include using Emacs shell-mode, which prompts
+you locally for the password when it recognizes a password prompt and then
+sends the whole password at once, and copying and pasting the password from
+somewhere else.
+
+As you'd expect, this password also takes a little while longer to type: about
+6 seconds instead of about 3 seconds.
+
+"""
+
+import random, itertools, os, sys
+
+def main(argv):
+ try:
+ nwords = int(argv[1])
+ except IndexError:
+ return usage(argv[0])
+
+ try:
+ nbits = int(argv[2])
+ except IndexError:
+ nbits = 11
+
+ filename = os.path.join(os.environ['HOME'], 'opt', 'share', 'wordlist')
+ wordlist = read_file(filename, nbits)
+ if len(wordlist) != 2**nbits:
+ sys.stderr.write("%r contains only %d words, not %d.\n" %
+ (filename, len(wordlist), 2**nbits))
+ return 2
+
+ display_password(generate_password(nwords, wordlist), nwords, nbits)
+ return 0
+
+def usage(argv0):
+ p = sys.stderr.write
+ p("Usage: %s nwords [nbits]\n" % argv0)
+ p("Generates a password of nwords words, each with nbits bits\n")
+ p("of entropy, choosing words from the first entries in\n")
+ p("$HOME/devel/wordlist, which should be in the same format as\n")
+ p("<http://canonical.org/~kragen/sw/wordlist>, which is a text file\n")
+ p("with one word per line, preceded by its frequency, most frequent\n")
+ p("words first.\n")
+ p("\nRecommended:\n")
+ p(" %s 5 12\n" % argv0)
+ p(" %s 6\n" % argv0)
+ return 1
+
+def read_file(filename, nbits):
+ return [line.split()[1] for line in
+ itertools.islice(open(filename), 2**nbits)]
+
+def generate_password(nwords, wordlist):
+ choice = random.SystemRandom().choice
+ return ' '.join(choice(wordlist) for ii in range(nwords))
+
+def display_password(password, nwords, nbits):
+ print 'Your password is "%s".' % password
+ entropy = nwords * nbits
+ print "That's equivalent to a %d-bit key." % entropy
+ print
+
+ # My Celeron E1200
+ # (<http://ark.intel.com/products/34440/Intel-Celeron-Processor-E1200-(512K-Cache-1_60-GHz-800-MHz-FSB)>)
+ # was released on January 20, 2008. Running it in 32-bit mode,
+ # john --test (<http://www.openwall.com/john/>) reports that it
+ # can do 7303000 MD5 operations per second, but I’m pretty sure
+ # that’s a single-core number (I don’t think John is
+ # multithreaded) on a dual-core processor.
+ t = years(entropy, 7303000 * 2)
+ print "That password would take %.2g CPU-years to crack" % t
+ print "on my inexpensive Celeron E1200 from 2008,"
+ print "assuming an offline attack on a MS-Cache hash,"
+ print "which is the worst password hashing algorithm in common use,"
+ print "slightly worse than even simple MD5."
+ print
+
+ t = years(entropy, 3539 * 2)
+ print "The most common password-hashing algorithm these days is FreeBSD’s"
+ print "iterated MD5; cracking such a hash would take %.2g CPU-years." % t
+ print
+
+ # (As it happens, my own machines use Drepper’s SHA-2-based
+ # hashing algorithm that was developed to replace the one
+ # mentioned above; I am assuming that it’s at least as slow as the
+ # MD5-crypt.)
+
+ # <https://en.bitcoin.it/wiki/Mining_hardware_comparison> says a
+ # Core 2 Duo U7600 can do 1.1 Mhash/s (of Bitcoin) at a 1.2GHz
+ # clock with one thread. The Celeron in my machine that I
+ # benchmarked is basically a Core 2 Duo with a smaller cache, so
+ # I’m going to assume that it could probably do about 1.5Mhash/s.
+ # All common password-hashing algorithms (the ones mentioned
+ # above, the others implemented in John, and bcrypt, but not
+ # scrypt) use very little memory and, I believe, should scale on
+ # GPUs comparably to the SHA-256 used in Bitcoin.
+
+ # The same mining-hardware comparison says a Radeon 5870 card can
+ # do 393.46 Mhash/s for US$350.
+
+ print "But a modern GPU can crack about 250 times as fast,"
+ print "so that same iterated MD5 would fall in %.1g GPU-years." % (t / 250)
+ print
+
+ # Suppose we depreciate the video card by Moore’s law,
+ # i.e. halving in value every 18 months. That's a loss of about
+ # 0.13% in value every day; at US$350, that’s about 44¢ per day,
+ # or US$160 per GPU-year. If someone wanted your password as
+ # quickly as possible, they could distribute the cracking job
+ # across a network of millions of these cards. The cards
+ # additionally use about 200 watts of power, which at 16¢/kWh
+ # works out to 77¢ per day. If we assume an additional 20%
+ # overhead, that’s US$1.45/day or US$529/GPU-year.
+ cost_per_day = 1.45
+ cost_per_crack = cost_per_day * 365 * t
+ print "That GPU costs about US$%.2f per day to run in 2011," % cost_per_day
+ print "so cracking the password would cost about US$%.1g." % cost_per_crack
+
+def years(entropy, crypts_per_second):
+ return float(2**entropy) / crypts_per_second / 86400 / 365.2422
+
+if __name__ == '__main__':
+ sys.exit(main(sys.argv))