#!/usr/bin/python

# Instructions on how to use:
#
# After a PGP keysigning party,
# 1) back up your current keyring (~/.gnupg) or use a separate account, then
# 2) create a new file with the 4-byte key IDs of the keys from the party, 1 per line.
# 3) Use it to verify the fingerprints of the people you met and sign their keys, such as:
#    for i in `cat keyids`; { gpg --edit-key "$i"; }
# 4) Edit the default values below to have your name, e-mail address, and key ID
# 5) Verify that this script does what it claims
# 6) Run the script, possibly with setting up gpg-agent first
# 7) It will extract the UIDs, encrypt them, and put them in e-mail files
# 8) Check over the e-mails, and send them out, such as:
#    for i in *.eml; { sendmail -t "$i"; }
# 9) Restore your keyring to its initial state and wait for people to upload
#    your signatures to the keyservers and send their signatures to you

#Obviously, you should check through this script and make sure it does
#what it claims, since you are trusting it with touching your PGP keyring.
#Any comments to help make it cleaner and more readable are welcome.

#This script splits up GPG keys into UIDs with just the self-signature and your signature,
#and put as e-mails with that key encrypted as an attachment.
#It uses a temporary directory for scratch work.
#This file generally contains much hackery.  Ideally, I would be using an OpenPGP library.
#Specifically, it runs the shell a lot, which can be very dangerous if any input is misformatted.

import base64
import os
import popen2
import re
import shutil
import textwrap

#Fill these in with your information
mykeyid1="AB4871FB"
myname="Sterling Stein"
myemail="scubed@lingcog.iit.edu" # From the primary e-mail address on your PGP key
print "You didn't set your key information at the top of the script!"
exit(1)

#Arbitrary values
boundary="----=_boundary_9864574797621"  #I should probably do this properly, but this is good enough for now
tmpdir="temporary_directory"
tmpfile="temporary_file"

#This is the template for the e-mail being sent
#(doesn't currently try to extract recipient's name, just key ID for now)
emailtemplate="""From: %(myname)s <%(myemail)s>
To: %(keyid)s <%(email)s>
Subject: GPG signed key
MIME-Version: 1.0
Content-Type: multipart/mixed;
  boundary="%(boundary)s"

--%(boundary)s
Content-Type: text/plain; charset=ISO-8859-1
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

Your signed key is attached and encrypted to you.
Please decrypt and import it.
For example:
  gpg --decrypt sig.gpg | gpg --import-options merge-only --import

--%(boundary)s
Content-Type: application/pgp-encrypted;
  name="sig.gpg"
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
  filename="sig.gpg"

%(attachment)s

--%(boundary)s--
"""

mykeyid=base64.b16decode(mykeyid1)
filecount=0

#Precompile regular expressions so don't waste time doing it every time
filesuffix=re.compile("^[0-9]*-([0-9]{3}[.][^.]*)$")
emailaddr=re.compile("^.*?<([!#$%&'*+/=?^_`.{|}~a-zA-Z0-9-]+@[.a-zA-Z0-9-]+)>.*$",re.DOTALL)

#A simple, but very unsafe, way to run shell commands and get their output
def easyrun(cmd):
  r,w,e=popen2.popen3(cmd)
  w.close()
  e.close()
  m=r.read()
  r.close()
  return m

def readfile(filename):
  f=open(filename,"rb")
  g=f.read()
  f.close()
  return g

def writefile(filename,data):
  f=open(filename,"w")
  f.write(data)
  f.close()

#Save to file instead of sending for now
#Then, after checking over,
#for i in *.eml; { sendmail -t "$i"; }
def writeemail(msg):
  global filecount
  writefile(("../%05d.eml" % (filecount)),msg)
  filecount+=1

def handlesig(keyid, publickey, userid, sigs, selfsig):
  if publickey==None or userid==None or len(sigs)==0 or selfsig==None:
    return

  keyring="".join(sigs)
  keyring="".join([publickey,userid,selfsig,keyring])
  writefile(tmpfile,keyring)

  addr=easyrun("< "+tmpfile+" gpg --verbose")
  addr=emailaddr.match(addr)
  if addr==None: return
  addr=addr.group(1)
  if addr==myemail: return

  print "  "+addr

  exportsig=easyrun("< "+tmpfile+" gpg --compress-algo none -se -r "+keyid+" -r "+mykeyid1)
  if len(exportsig)<80:
    print "Warning: Can't encrypt, skipping"
    return

  exportsig=textwrap.fill(base64.b64encode(exportsig))
  msg=emailtemplate % {
    "myname": myname,
    "myemail": myemail,
    "boundary": boundary,
    "keyid": keyid,
    "email": addr,
    "attachment": exportsig,
  }

  writeemail(msg)

#Since I am signing in 1 particular way,
#I know the exact offset of my key ID is 22
#If it's a longer packet, it might be at 23 (due to 2 byte length instead of 1)
def ismysig(filename):
  f=open(filename,"rb")
  g=f.read()
  f.close()
  if len(g)!=72:
    return False
  return g[22:26]==mykeyid

def extractsubkeys(keyid):
  #Pass 1: get uids and their self-sigs
  os.mkdir(tmpdir)
  os.chdir(tmpdir)
  easyrun("gpg --export --export-options export-minimal "+keyid+" | gpgsplit")
  files=os.listdir('.')
  files.sort()

  publickey=None
  selfsigs={}
  uidpos=2
  for f in files:
    uidpos+=1
    suffix=filesuffix.match(f)
    if suffix==None: continue
    suffix=suffix.group(1)

    if suffix=="006.public_key":
      publickey=readfile(f)
      continue

    if suffix=="013.user_id":
      userid=f
      uidpos=0
      continue

    if suffix!="002.sig":
      continue

    #Self-sig is immediately after UID, ignore anything else
    if uidpos==1:
      selfsigs[readfile(userid)]=readfile(f)

  os.chdir("..")
  shutil.rmtree(tmpdir)


  #Pass 2: Get my signature
  os.mkdir(tmpdir)
  os.chdir(tmpdir)
  easyrun("gpg --export "+keyid+" | gpgsplit")
  files=os.listdir('.')
  files.sort()

  userid=None
  mysigs=[]
  for f in files:
    suffix=filesuffix.match(f)
    if suffix==None: continue
    suffix=suffix.group(1)

    if suffix=="013.user_id":
      if userid in selfsigs:
        handlesig(keyid, publickey, userid, mysigs, selfsigs[userid])
      mysigs=[]
      userid=readfile(f)
      continue

    if suffix!="002.sig":
      continue

    if ismysig(f):
      mysigs.append(readfile(f))
      continue

  if userid in selfsigs:
    handlesig(keyid, publickey, userid, mysigs, selfsigs[userid])

  os.chdir("..")
  shutil.rmtree(tmpdir)

def main():
  keyidlist=readfile("keyids").split()
  for keyid in keyidlist:
    print keyid
    extractsubkeys(keyid)

main()
