summaryrefslogtreecommitdiff
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
parentac5044074e01f518ae3c837dfad0fa3fc0719962 (diff)
Adding files from bismuth
-rwxr-xr-xical_reply_sendmail_wrapper.sh12
-rwxr-xr-xmkpasswd165
-rwxr-xr-xmutt-ical.py149
3 files changed, 326 insertions, 0 deletions
diff --git a/ical_reply_sendmail_wrapper.sh b/ical_reply_sendmail_wrapper.sh
new file mode 100755
index 0000000..24a3db4
--- /dev/null
+++ b/ical_reply_sendmail_wrapper.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+# get $sendmail from mutt
+SENDMAIL=`mutt -D|awk -F= '/^sendmail=/{print $2}' | sed -s 's/"//g'`
+
+# fix header for ical reply and pipe to sendmail
+sed '/^Content-Type: text\/calendar;/s/$/; METHOD="REPLY"/'|
+sed '/^Content-Disposition: attachment/s/attachment/inline/g'|
+$SENDMAIL "$@"
+
+# this may also be necessary:
+#sed '/^Content-Type: multipart\/mixed/s/mixed/alternative/g'|
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))
diff --git a/mutt-ical.py b/mutt-ical.py
new file mode 100755
index 0000000..69ff4b2
--- /dev/null
+++ b/mutt-ical.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python
+# -*- coding: utf8 -*-
+
+"""
+This script is meant as a simple way to reply to ical invitations from mutt.
+See README for instructions and LICENSE for licensing information.
+"""
+
+from __future__ import with_statement
+
+__author__="Martin Sander"
+__license__="MIT"
+
+
+import vobject
+import tempfile, time
+import os, sys
+import warnings
+from datetime import datetime
+from subprocess import Popen, PIPE
+from getopt import gnu_getopt as getopt
+
+usage="""
+usage:
+%s [OPTIONS] -e your@email.address filename.ics
+OPTIONS:
+ -i interactive
+ -a accept
+ -d decline
+ -t tentatively accept
+ (accept is default, last one wins)
+""" % sys.argv[0]
+
+def del_if_present(dic, key):
+ if dic.has_key(key):
+ del dic[key]
+
+def set_accept_state(attendees, state):
+ for attendee in attendees:
+ attendee.params['PARTSTAT'][0] = state
+ for i in ["RSVP","ROLE","X-NUM-GUESTS","CUTYPE"]:
+ del_if_present(attendee.params,i)
+ return attendees
+
+def get_accept_decline():
+ while True:
+ sys.stdout.write("Accept Invitation? [Y/n/t]")
+ ans = sys.stdin.readline()
+ if ans.lower() == 'y\n' or ans == '\n':
+ return 'ACCEPTED'
+ elif ans.lower() == 'n\n':
+ return 'DECLINED'
+ elif ans.lower() =='t\n':
+ return 'TENTATIVE'
+
+def get_answer(invitation):
+ # create
+ ans = vobject.newFromBehavior('vcalendar')
+ ans.add('method')
+ ans.method.value = "REPLY"
+ ans.add('vevent')
+
+ # just copy from invitation
+ for i in ["uid", "summary", "dtstart", "dtend", "organizer"]:
+ if invitation.vevent.contents.has_key(i):
+ ans.vevent.add( invitation.vevent.contents[i][0] )
+
+ # new timestamp
+ ans.vevent.add('dtstamp')
+ ans.vevent.dtstamp.value = datetime.utcnow().replace(
+ tzinfo = invitation.vevent.dtstamp.value.tzinfo)
+ return ans
+
+def write_to_tempfile(ical):
+ tempdir = tempfile.mkdtemp()
+ icsfile = tempdir+"/event-reply.ics"
+ with open(icsfile,"w") as f:
+ f.write(ical.serialize())
+ return icsfile, tempdir
+
+def get_mutt_command(ical, email_address, accept_decline, icsfile):
+ accept_decline = accept_decline.capitalize()
+ sender = ical.vevent.contents['organizer'][0].value.split(':')[1].encode()
+ summary = ical.vevent.contents['summary'][0].value.encode()
+ command = ["mutt", "-a", icsfile,
+ "-e", 'set sendmail=\'ical_reply_sendmail_wrapper.sh\'',
+ "-s", "'%s: %s'" % (accept_decline, summary), "--", sender]
+ return command
+
+def execute(command, mailtext):
+ process = Popen(command, stdin=PIPE)
+ process.stdin.write(mailtext)
+ process.stdin.close()
+
+ result = None
+ while result is None:
+ result = process.poll()
+ time.sleep(.1)
+ if result != 0:
+ print "unable to send reply, subprocess exited with\
+ exit code %d\nPress return to continue" % result
+ sys.stdin.readline()
+
+if __name__=="__main__":
+ email_address = None
+ accept_decline = 'ACCEPTED'
+ opts, args=getopt(sys.argv[1:],"e:aidt")
+ for opt,arg in opts:
+ if opt == '-e':
+ email_address = arg
+ if opt == '-i':
+ accept_decline = get_accept_decline()
+ if opt == '-a':
+ accept_decline = 'ACCEPTED'
+ if opt == '-d':
+ accept_decline = 'DECLINED'
+ if opt == '-t':
+ accept_decline = 'TENTATIVE'
+
+ if len(args) < 1 or not email_address:
+ sys.stderr.write(usage)
+ sys.exit(1)
+
+ invitation_file = args[0]
+ with open(invitation_file) as f:
+ try:
+ with warnings.catch_warnings(): #vobject uses deprecated Exception stuff
+ warnings.simplefilter("ignore")
+ invitation = vobject.readOne(f, ignoreUnreadable=True)
+ except AttributeError:
+ invitation = vobject.readOne(f, ignoreUnreadable=True)
+
+ ans = get_answer(invitation)
+
+ attendees = invitation.vevent.contents['attendee']
+ set_accept_state(attendees,accept_decline)
+ ans.vevent.contents['attendee'] = [i for i in attendees if i.value.endswith(email_address)]
+ if len(ans.vevent.contents) < 1:
+ sys.stderr.write("Seems like you have not been invited to this event!\n")
+ sys.exit(1)
+
+ icsfile, tempdir = write_to_tempfile(ans)
+
+ mutt_command = get_mutt_command(ans, email_address, accept_decline, icsfile)
+ mailtext = "'%s has %s'" % (email_address, accept_decline.lower())
+ execute(mutt_command, mailtext)
+
+ os.remove(icsfile)
+ os.rmdir(tempdir)