# File: gpg.py # Author: Toshio Kuratomi # Date: 27 Nov 2004 # Copyright: Toshio Kuratomi # License: GPL # Id: $Id: gpg.py 188 2005-06-14 00:55:59Z badger $ '''Invoke gpg from here. Eventually we may want all of these to be superceded by gpgme calls. OTOH gpgme enables C code to use gpg the way a scripting language would. Since python is a glue language anyhow.... ''' __revision__ = '$Rev: 188 $' import os import error # With gpgme, this function remains as the get passphrase callback. # Use gpgme_set_passphrase_cb to set it. import gtk def get_passphrase(gpgId, badPass): '''Retrieve a passphrase from the user. Arguments: :gpgId: Id to send to gpg as the user requesting the signature :badPass: True if this is a repeat request for the password Return: The passphrase entered by the user. ''' passDialog = gtk.Dialog('Enter Passphrase', None, gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_OK, gtk.RESPONSE_OK, gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)) passDialog.set_default_response(gtk.RESPONSE_OK) vbox = passDialog.vbox # Let the user know if the previous password didn't work. if badPass: reprompt = gtk.Label('The password you entered did not work. Please' ' try reentering it.') reprompt.set_line_wrap(True) vbox.add(reprompt) prompt = gtk.Label('Please enter the passphrase for ' + gpgId) prompt.set_line_wrap(True) vbox.add(prompt) passphraseEntry = gtk.Entry() passphraseEntry.set_visibility(False) vbox.add(passphraseEntry) vbox.show_all() response = passDialog.run() if response == gtk.RESPONSE_OK: passphrase = passphraseEntry.get_text() else: passphrase = None passDialog.destroy() return passphrase def list_secret_keys(pathToGpg): ''' List the secret key identities available for the user. Arguments :pathToGpg: Path to the gpg program binary. Returns: A list of the secret keys suitable for signing with. ''' if not (os.path.isfile(pathToGpg) and os.access(pathToGpg, os.X_OK)): raise error.NotGPGCompatible, 'The specified program does not exist.' secretKeyList = [] cmd = pathToGpg + ' --list-secret-keys --with-colons' gpgCmd = os.popen(cmd) for record in gpgCmd.readlines(): recordArray = record.split(':') if recordArray[0] == 'sec': secretKeyList.append(recordArray[9]) if gpgCmd.close(): raise error.NotGPGCompatible, ('The signing program selected in the' ' Preferences is not command line compatible with gpg.') return secretKeyList def sign_buffer(buf, gpgID, pathToGpg, passphrase): '''Sign an in-memory buffer. Arguments: :buf: Buffer to sign. :gpgID: Person whose key we are going to sign with. :pathToGpg: Path to the gpg program. :passphrase: The passphrase to unlock the gpg key. Returns: The signed buffer. ''' if not (os.path.isfile(pathToGpg) and os.access(pathToGpg, os.X_OK)): raise error.NotGPGCompatible, 'The specified program does not exist.' cmd = (pathToGpg + " --local-user '" + gpgID + "' --clearsign --armor --status-fd 2 --command-fd 0") (gpgIn, gpgOut, gpgStatus) = os.popen3(cmd) gpgString = gpgStatus.readline().split(None, 2) if (gpgString[0] != 'gpg:') and (gpgString[0] != '[GNUPG:]'): raise error.NotGPGCompatible, ('The signing program selected in the' ' Preferences is not gpg compatible.') while True: if gpgString[1] == 'GET_HIDDEN': gpgIn.writelines((passphrase, '\n')) gpgIn.flush() elif gpgString[1] == 'GOOD_PASSPHRASE': gpgIn.write(buf) gpgIn.close() elif gpgString[1] == 'SIG_CREATED': outBuf = gpgOut.read() break elif gpgString[1] == 'BAD_PASSPHRASE': gpgIn.close() gpgOut.close() gpgStatus.close() raise error.BadPassphrase, ('The entered passphrase was not valid.') elif gpgString[1] == 'NO_SECKEY' or (gpgString[1] == 'skipped' and gpgString[2] == "`" + gpgID + "': secret key not available\n"): gpgIn.close() gpgOut.close() gpgStatus.close() raise error.NoSecretKey, ('No secret key for %s' % gpgID) gpgString = gpgStatus.readline().split(None, 2) gpgIn.close() gpgOut.close() gpgStatus.close() if not outBuf: raise error.NoOut, ('No output generated by GPG signing!') return outBuf