keyring-splitting program

JanuszA.Urbanowicz JanuszA.Urbanowicz
Sun Mar 24 18:41:02 2002


--ELM72123749-26264-0_
Content-Type: application/pgp; format=text; x-action=sign
Content-Transfer-Encoding: 8bit

-----BEGIN PGP SIGNED MESSAGE-----
Hash: RIPEMD160

Hello

I've written a small Python program/module to split my keyring into sepaarte
keys for rebuilding since it got corrupted. It is also useful for importing
large amounts of keys into PGP - all versions have problems with importing
large (>50) solid blobs of public keys. Since its a spinoff of generic gnupg
gnupg interface I'm writing, I named id gnupg.py, feel encouraged to rename
it as you like during installation.

I consider this a submission for the Project's tools directory.

Hope you will find the code useful.

Alex
- -- 
Janusz A. Urbanowicz | ALEX3-RIPE | SF-Framling | Thawte Web Of Trust Notary

Gdy daję biednym chleb, nazywają mnie świętym. Gdy pytam, 
dlaczego biedni nie mają chleba, nazywają mnie komunistą. - abp. Helder Camara
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6d (GNU/Linux)

iD8DBQE8nhEdTfkBjn4ugD0RA+hJAJ9f/Zuv5kFG3EDkY2+sLmHYCgQ9lgCeJajl
V69/47wLvRSE/82UUyaOw4o=
=OZVm
-----END PGP SIGNATURE-----

--ELM72123749-26264-0_
Content-Type: text/plain; charset=ISO-8859-2
Content-Disposition: attachment; filename=gnupg.py
Content-Description: python program
Content-Transfer-Encoding: 7bit

#! /usr/bin/env python

'''Utility program for GNU Privacy Guard.

   This code runs under Python 1.5.2 on GNU/Linux. It should run on newer
   versions of Python as well. Some of the functions won't run on other
   architectures.

   Copyright 2002 Janusz A. Urbanowicz <alex@bofh.net.pl>

   $Id: gnupg.py,v 1.2.1.5 2002/03/24 17:16:07 alex Exp $

   This program is distributed under the terms opf GNU General Public License.
   Version 2 or later.

   When called as a program, exports every key in the public keyring to
   a file named by the key's 64 bit key ID in binary or ascii armored form.

   Optionally, binary mode and destination directory can be specified.
   For usage info, run the program with -h parameter.

   The program can be laso used as a Python module with following public
   functions: listkeys(), listkey(), simpledecrypt() and recipients().
   For description of those, refer to appropriate docstrings.
'''

import tempfile,os,re,popen2,sys,getopt,os.path


#
# Utility functions
#

def listkeys():
  "Returns a dictionary of long-keyid (key) and primary UID (value) for default keyring)"

  primaryre = re.compile('^pub:(?:-|u|r|f|d|m|q|e):[0-9]{3,4}?:(?:1|17):((?:[A-Z]|[0-9])+?):[0-9]{4}\-[0-9]{2}\-[0-9]{2}:(?:[0-9]{4}\-[0-9]{2}\-[0-9]{2})?:\w*?:\w*?:(.*?):\w*?:\w*?:')
  secondaryre = re.compile('^uid:(?:-|u|r|f|d|m|q|e)::::::::(.*?):')
  imagere = re.compile('^\[image of size [0-9]+?\]')

  klucze = {}
  
  gpghandle = os.popen('gpg --with-colons -k','r')
  gpgoutput = gpghandle.readlines()
  gpghandle.close()
  
  for i in range(len(gpgoutput)):
    result = primaryre.match(gpgoutput[i])
    try:
      keyid = result.groups()[0]
    except AttributeError:
      continue
    klucze[keyid] = result.groups()[1]
    if imagere.match(klucze[keyid]) == None:
      continue
    else:
      klucze[keyid] = None
      while primaryre.match(gpgoutput[i+1]) == None:
        i = i+1
        result = secondaryre.match(gpgoutput[i])
        try:
          klucze[keyid] = result.groups()[0]
          assert imagere.match(klucze[keyid]) == None
          break
        except AttributeError,AssertionError:
          pass
      if klucze[keyid] == None:
        del klucze[keyid]
        del keyid
        
  return klucze

def listkey(key):
  "Returns a dictionary of long-keyid (key) and primary UID (value) for a single key."

  primaryre = re.compile('^pub:(?:-|u|r|f|d|m|q|e):[0-9]{3,4}?:(?:1|17):((?:[A-Z]|[0-9])+?):[0-9]{4}\-[0-9]{2}\-[0-9]{2}:(?:[0-9]{4}\-[0-9]{2}\-[0-9]{2})?:\w*?:\w*?:(.*?):\w*?:\w*?:')
  secondaryre = re.compile('^uid:(?:-|u|r|f|d|m|q|e)::::::::(.*?):')
  imagere = re.compile('^\[image of size [0-9]+?\]')

  klucze = {}
  
  gpghandle = os.popen('gpg --with-colons -k %s' % key,'r')
  gpgoutput = gpghandle.readlines()
  gpghandle.close()
  
  for i in range(len(gpgoutput)):
    result = primaryre.match(gpgoutput[i])
    try:
      keyid = result.groups()[0]
    except AttributeError:
      continue
    klucze[keyid] = result.groups()[1]
    if imagere.match(klucze[keyid]) == None:
      continue
    else:
      klucze[keyid] = None
      while primaryre.match(gpgoutput[i+1]) == None:
        i = i+1
        result = secondaryre.match(gpgoutput[i])
        try:
          klucze[keyid] = result.groups()[0]
          assert imagere.match(klucze[keyid]) == None
          break
        except AttributeError,AssertionError:
          pass
      if klucze[keyid] == None:
        del klucze[keyid]
        del keyid
        
  return klucze

def simpledecrypt(passphrase,data):
  'Decrypt PGP encrypted data with supplied passphrase, the result is the return value'
  
  tempfilename = tempfile.mktemp()
  tempfile = open(tempfilename,'w')
  tempfile.write(data)
  tempfile.close()

  gpghandle = popen2.popen2('gpg --passphrase-fd 0 --batch --output - %s' % tempfilename)
  gpghandle[1].write(passphrase)
  gpghandle[1].close()
  data = gpghandle[0].read()
  gpghandle[0].close()

  os.unlink(tempfilename)
  
  return data

def recipients(data):
  "Returns a tuple of key IDs the message is encrypted to. Based on the code from GnuPG FAQ."

  gpghandle = popen2.popen2('gpg --batch --decrypt --list-only --status-fd 1')
  gpghandle[1].write(data)
  gpghandle[1].close()
  results = gpghandle[0].readlines()
  gpghandle[0].close()

  recipientre = re.compile('^\[GNUPG:\] ENC_TO ((?:[A-Z]|[0-9]){16}) .*$',re.MULTILINE)

  result = []

  for line in results:
    try:
      match = recipientre.search(line).groups()
    except AttributeError:
      continue

    result.append(match[0])

  return tuple(result)
  
#
# Splitkeyring code.
#

if __name__ == '__main__':

  dir = ''
  mode = '--armor'
  
  params,args = getopt.getopt(sys.argv[1:],'hd:b',['help','output-dir','binary'])
  
  for param in params:
    if '-h' in param or '--help' in param:
      print '%s $Revision: 1.2.1.5 $\n' % sys.argv[0]
      print 'Copyright 2002 Janusz A. Urbanowicz <alex@bofh.net.pl>.\n'
      print 'This program is distributed under the terms of GNU General Public License.'
      print 'Version 2 or later.\n'
      print 'Synopsis: %s [-d | --output-dir <output dir>] [-b | --binary]' % sys.argv[0]
      print '\nOptions: \n\n   -d, --output-dir   directtory to store the keys in (default: cwd)'
      print '   -b, --binary       export keys in binary form (default: off)'
      print '   -h, --help         this help message\n'
      sys.exit(0)

    if '-d' in param or '--output-dir' in param:
      try:
        dir = param[1]
      except:
        print 'No output directory supplied.'
        sys.exit(1)
      dir = os.path.abspath(dir)

    if '-b' in param or '--binary' in param:
      mode = ''

  lkeys = listkeys()

  for keyid in lkeys.keys():
    file = open(os.path.join(dir,keyid),'w+')
    if mode:
      file.write('%s\n\n' % lkeys[keyid])
    gpghandle = os.popen('gpg --quiet %s --batch --export %s' % (mode, keyid), 'r')
    data = gpghandle.readlines()
    gpghandle.close()
    file.writelines(data)
    file.close()


## Local Variables:
## mode: Python
## py-indent-offset: 2
## fill-column: 100
## End:


--ELM72123749-26264-0_
Content-Type: application/pgp
Content-Disposition: attachment; filename=gnupg.py.asc
Content-Description: detached signature file
Content-Transfer-Encoding: 7bit

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.6d (GNU/Linux)
Comment: http://quiston.tpsa.com/~alex/pgp.html

iQCVAwUAPJ4Qerm/3a0nyBvJAQPbMQQAhKJQNmwYrEwd5Gk0dOJfvGL+UZM+aD4Q
FPHneoXj137/8fN+/0BgOY6FjRoAFxr1jJ6ErTO/INBJA5xulR6yhZNW5qRDDl/a
KuxzC1ptCcnFQNLNkUdbIMsqzjErPCffBtrgkMbg/ubTNPutKRNn3c8E+cFzFv24
i5TsumSSvZA=
=6beb
-----END PGP SIGNATURE-----

--ELM72123749-26264-0_--