#!/usr/bin/python # vim:shiftwidth=2:tabstop=2:expandtab:textwidth=78:softtabstop=2:ai: # # This script was made from a shoddy group of shell scripts that tried to make # this work easier. However, gpg doesn't give the nicest interface for # automation, and this evolved into a much more complex script. # # Wherever possible we use the --*-fd options to gpg for automation rather # than screenscraping (using pexpect) since it's more resiliant. However, # until working with gpg-agent is full working the 'interactive' mode (which # requires pexpect) will remain in the code. # # Note that the only way to specify a UID is in --edit-key, without --batch. # # Copyright (c) 2009 Phil Dibowitz (phil@ipom.com) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation, version 2. # # TODO: # - Offer ability to "pick up where we left off" # from email import MIMEBase from email import MIMEMultipart from email import MIMEText from email.Utils import formatdate import getpass from optparse import OptionParser import os import pexpect import re import smtplib import socket import subprocess import sys import time VERSION = '1.2' debug_on = False MODE_INTERACTIVE = 0 MODE_CACHE_PASSPHRASE = 1 MODE_AGENT = 2 DEFAULT_GPG_PATH = '/usr/bin/gpg' DEFAULT_KEYRING = '~/.gnupg/pubring.gpg' DEFAULT_TMP_DIR = '/tmp' DEFAULT_OUT_DIR = '/tmp' DEFAULT_EMAIL_TEXT = '''Hello, Attached is a copy of your PGP key (0x%s) signed by my key (0x%s). If your key has more than one UID, than this key only has the UID associated with this email address (%s) signed and you will receive additional emails containing signatures of the other UIDs at the respective email addresses. Please take the attached message and decrypt it and then import it. Something like this should work: gpg -d | gpg --import If you have any questions, let me know. ''' def debug(line): '''Print a line, if debug is on, preceeded with DEBUG:.''' if debug_on: print 'DEBUG:', line def print_default_email(): print 'DEFAULT EMAIL TEXT:\n' print (DEFAULT_EMAIL_TEXT % ('', '', '')) class AgentError(Exception): '''An exception for when Agent sucks.''' pass class PassphraseError(Exception): '''An exception for when a 'good' cached passphrase didn't work.''' pass class EncryptionKeyError(Exception): '''An exception for when a key can't encrypt (no encryption subkey).''' pass class EncryptionUnknownError(Exception): '''An exception for for NOT the above. Should never happen.''' pass class uids_signer(object): '''Main class for signing UIDs.''' TMP_KEYRING_FILE = 'pius_keyring.asc' GPG_PROMPT = '[GNUPG:] GET_LINE keyedit.prompt' GPG_ACK = '[GNUPG:] GOT_IT' GPG_ALREADY_SIGNED = '[GNUPG:] ALREADY_SIGNED' GPG_CONFIRM = '[GNUPG:] GET_BOOL sign_uid.okay' GPG_SAVE = '[GNUPG:] GET_BOOL keyedit.save.okay' GPG_ENC_BEG = '[GNUPG:] BEGIN_ENCRYPTION' GPG_ENC_END = '[GNUPG:] END_ENCRYPTION' GPG_ENC_INV = '[GNUPG:] INV_RECP' def __init__(self, signer, mode, keyring, gpg_path, tmpdir, outdir, encrypt_outfiles, sign_level, mail, verbose, mail_text, override_email): self.mode = mode self.signer = signer self.keyring = keyring self.passphrase_confirmed = False self.gpg = gpg_path self.tmpdir = tmpdir self.outdir = outdir self.encrypt_outfiles = encrypt_outfiles self.sign_level = sign_level self.mail = mail self.mail_text = mail_text self.verbose = verbose self.override_email = override_email self.tmp_keyring = '%s/%s' % (self.tmpdir, uids_signer.TMP_KEYRING_FILE) self.gpg_quiet_opts = '-q --no-tty --no-auto-check-trustdb --batch' self.gpg_fd_opts = '--command-fd 0 --passphrase-fd 0 --status-fd 1' def cleanup(self): '''Cleanup all our temp files.''' self._clean_files([self.tmp_keyring, ('%s~' % self.tmp_keyring)]) def _clean_files(self, list): '''Delete a list of files.''' for file in list: if os.path.exists(file): os.unlink(file) def get_all_keyids(self): '''Given a keyring, get all the KeyIDs from it.''' debug('extracting all keyids from keyring') cmd = ('%s --no-default-keyring --keyring %s --no-options --with-colons' ' --fingerprint' % (self.gpg, self.keyring)) debug('cmd') gpg = os.popen(cmd, 'r') pub_re = re.compile('^pub:') keyids = [] for line in gpg.readlines(): if not pub_re.search(line): continue id = line.split(':')[4] # get the shirt version id = id[8:16] debug('Got id %s' % id) keyids.append(id) gpg.close() return keyids def check_fingerprint(self, key): '''Prompt the user to see if they have verified this fingerprint.''' cmd = ('%s --no-default-keyring --keyring %s --fingerprint %s' % (self.gpg, self.keyring, key)) gpg = os.popen(cmd, 'r') for line in gpg.readlines(): if line != '\n': print line.strip() gpg.close() a = raw_input('Have you verified this user/key? (y/N/q) ') print if a in ('y', 'Y', 'yes', 'Yes', 'YES'): return True; elif a in ('q', 'Q'): print 'Dying at user request' sys.exit(1) return False def get_passphrase(self): '''Prompt the user for their passphrase.''' self.passphrase = getpass.getpass('Please enter your PGP passphrase: ') def verify_passphrase(self): '''Verify a passpharse gotten from get_passpharse().''' magic_string = 'test1234' filename = '%s/pius_tmp' % self.tmpdir filename_enc = '%s/pius_tmp.gpg' % self.tmpdir filename_dec = '%s/pius_tmp2' % self.tmpdir self._clean_files([filename, filename_enc, filename_dec]) tfile = open(filename, 'w') tfile.write(magic_string) tfile.close() cmd = ('%s %s --always-trust -r %s -e %s' % (self.gpg, self.gpg_quiet_opts, self.signer, filename)) debug(cmd) gpg = os.popen(cmd, 'r') gpg.close() cmd = ('%s %s %s --output %s -d %s' % (self.gpg, self.gpg_quiet_opts, self.gpg_fd_opts, filename_dec, filename_enc)) debug(cmd) gpg = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) time.sleep(1) debug('Sending passphrase') gpg.stdin.write('%s\n' % self.passphrase) time.sleep(1) os.kill(gpg.pid, 15) debug('Waiting for result') gpg.wait() if not os.path.exists(filename_dec): debug('Resulting file %s not found' % filename_dec) return False tfile = open(filename_dec, 'r') line = tfile.readline() tfile.close() self._clean_files([filename, filename_enc, filename_dec]) if line == magic_string: self.passphrase_confirmed = True return True debug('File does not contain magic string') return False def get_uids(self, key): '''Get all UIDs on a given key.''' cmd = ('%s %s %s --no-default-keyring --keyring %s --no-options' ' --with-colons --edit-key %s' % (self.gpg, self.gpg_quiet_opts, self.gpg_fd_opts, self.keyring, key)) debug(cmd) gpg = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) gpg.stdin.write('\n') # We want the indexes to start the indexs at 1 (which is what pgp uses) # so we fill the 0th entry. uids = ['empty'] unique_files = [] while True: line = gpg.stdout.readline().strip() if not line: debug('breaking, EOF') break if line == uids_signer.GPG_PROMPT: debug('got to command prompt') break debug('Got a line %s' % line) fields = line.split(':') if 'uid' != fields[0]: continue status = fields[1] id = fields[9] debug('Got UID %s with status %s' % (id, status)) # If we can we capture an email address is saved for # emailing off signed keys (not yet implemented), and # also for the ID for that UID. # # If we can't, then we grab what we can and make it the # id and blank out the email. # # For the normal case (have email), we'll be storing each email twice # but that's OK since it means that email is *always* a vlaid email or # None and id is *always* a valid identifier m = re.search('.* <(.*)>', id) if m: email = m.group(1) debug('got email %s' % email) filename = re.sub('@', '_at_', email) filename = '%s__%s' % (key, filename) id = email else: # but if it doesn't have an email, do the right thing email = None debug('no email') id = re.sub(' ', '_', id) filename = '%s__%s' % (key, id) if filename in unique_files: debug('Filename is a duplicate') count = 2 while True: test = '%s_%s' % (filename, count) debug('Trying %s' % test) if test not in unique_files: debug('%s worked!' % test) filename = test break else: count += 1 else: debug('%s isn\'t in %s' % (filename, repr(unique_files))) # NOTE: Make sure to append the file BEFORE adding the extension # since that's what we test against above! unique_files.append(filename) filename += '.asc' uids.append({'email': email, 'file': filename, 'status': status, 'id': id}) debug('quitting') # sometimes it wants a save here. I don't know why. We can quit and check # for a save prompt, and then hit no, but we have to make sure it's still # running or we'll hang. It's just easier to issue a 'save' instead of a # quit gpg.stdin.write('save\n') debug('waiting') gpg.wait() return uids def nuke_working_keyring(self): '''Delete our temporariy working keyring.''' if os.path.exists(self.tmp_keyring): os.unlink(self.tmp_keyring) def encrypt_signed_uid(self, key, filename): '''Encrypt the file we exported the signed UID to.''' (base, ext) = os.path.splitext(filename) path = '%s/%s' % (self.outdir, filename) enc_path = '%s/%s_ENCRYPTED.asc' % (self.outdir, base) if os.path.exists(enc_path): os.unlink(enc_path) cmd = ('%s %s %s --no-default-keyring --keyring %s --always-trust --armor' ' -r %s --output %s -e %s' % (self.gpg, self.gpg_quiet_opts, self.gpg_fd_opts, self.tmp_keyring, key, enc_path, path)) debug(cmd) gpg = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) # Must send a blank line... gpg.stdin.write('\n') while True: debug('Waiting for response') line = gpg.stdout.readline().strip() debug('Got %s' % line) if uids_signer.GPG_ENC_BEG in line: debug('Got GPG_ENC_BEG') continue elif uids_signer.GPG_ENC_END in line: debug('Got GPG_ENC_END') break elif uids_signer.GPG_ENC_INV in line: debug('Got GPG_ENC_INV') raise EncryptionKeyError else: raise EncryptionUnknownError gpg.wait() return enc_path def _export_key(self, keyring, key, path): '''Internal function used by other export_* functions.''' if os.path.exists(path): os.unlink(path) cmd = ('%s %s --no-default-keyring --keyring %s --armor' ' --output %s --export %s' % (self.gpg, self.gpg_quiet_opts, keyring, path, key)) debug(cmd) gpg = os.popen(cmd, 'r') gpg.close() def export_signed_uid(self, key, filename): '''Export the signed UID form working keyring.''' path = '%s/%s' % (self.outdir, filename) debug('exporting %s' % key) self._export_key(self.tmp_keyring, key, path) def export_clean_key(self, key): '''Export clean key from the users' KeyID.''' debug('exporting %s' % key) # We have to export our own public key as well keys_to_export = '%s %s' % (key, self.signer) path = '%s/%s.asc' % (self.tmpdir, key) self._export_key(self.keyring, keys_to_export, path) def clean_clean_key(self, key): '''Delete the "clean" unsigned key which we exported temporarily.''' path = '%s/%s.asc' % (self.tmpdir, key) self._clean_files([path]) def import_clean_key(self, key): '''Import the clean key we expoerted in export_clean_key() to our temp keyring.''' path = '%s/%s.asc' % (self.tmpdir, key) cmd = ('%s %s --no-default-keyring --keyring %s' ' --import %s' % (self.gpg, self.gpg_quiet_opts, self.tmp_keyring, path)) debug(cmd) gpg = os.popen(cmd, 'r') gpg.close() def sign_with_interact(self, gpg): '''Tell the user how to get out of this, and then drop them into the gpg shell.''' print ' Passing you to gpg for passprhase.' print ' Hit ^] after succesfully typing in your passphrase' gpg.interact() # When we return, we have a Command> prompt that w can't # 'expect'... or at least if the user did it right print '' # # NOTE: # This currently doesn't work with gpg-agent very seemlessly. If you have # an agent on, and you DON'T specify -p, it'll work, but you have to hit # the 'special char' after each sign. If you do use -p, then we never get # a command-prompt and we get confused. I've yet to make htis work. # def sign_uid_expect(self, key, index): '''Sign a UID, using the expect stuff. Interactive mode.''' cmd = ('%s --no-default-keyring --keyring %s --default-cert-level %s' ' --no-ask-cert-level --edit-key %s' % (self.gpg, self.tmp_keyring, self.sign_level, key)) debug(cmd) gpg = pexpect.spawn(cmd) gpg.setecho(False) gpg.expect('Command> ') debug('Selecting UID') gpg.sendline(str(index)) gpg.expect('Command> ') debug('Running sign subcommand') gpg.sendline('sign') line = gpg.readline() if 'already signed' in line: print ' UID already signed' return False # else it's a blank line... gpg.expect(re.compile('Really sign.*')) debug('Confirming signing') gpg.sendline('y') self.sign_with_interact(gpg) # Unselect this UID debug('unselecting uid') #gpg.sendline(str(index)) #gpg.expect('Command> ') debug('Saving key') gpg.sendline('save') #gpg.close() return True def gpg_wait_for_string(self, fd, string): '''Look for a specific string on the status-fd.''' line = '' while line not in (string,): debug('Waiting for line') line = fd.readline().strip() debug('got line %s' % line) def sign_uid(self, key, index): '''Sign a single UID of a key. This can use either cached passpharse or gpg-agent.''' agent = '' if self.mode == MODE_AGENT: agent = '--use-agent' # Note that if passphrase-fd is different from command-fd, nothing works. cmd = ('%s %s %s --no-default-keyring --keyring %s %s' ' --default-cert-level %s --no-ask-cert-level --edit-key %s' % (self.gpg, self.gpg_quiet_opts, self.gpg_fd_opts, self.tmp_keyring, agent, self.sign_level, key)) debug(cmd) gpg = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, close_fds=True) if self.mode == MODE_AGENT: # For some reason when using agent an initial enter is needed gpg.stdin.write('\n') else: # For some unidentified reason you must send the passphrase # first, not when it asks for it. debug('Sending passphrase') gpg.stdin.write('%s\n' % self.passphrase) debug('Waiting for prompt') self.gpg_wait_for_string(gpg.stdout, uids_signer.GPG_PROMPT) debug('Selecting UID %d' % index) gpg.stdin.write('%s\n' % str(index)) debug('Waiting for ack') self.gpg_wait_for_string(gpg.stdout, uids_signer.GPG_ACK) debug('Running sign subcommand') self.gpg_wait_for_string(gpg.stdout, uids_signer.GPG_PROMPT) debug('Sending sign command') gpg.stdin.write('sign\n') self.gpg_wait_for_string(gpg.stdout, uids_signer.GPG_ACK) # Either we already signed this, or we can proceed line = gpg.stdout.readline() debug('Got %s' % line) if uids_signer.GPG_ALREADY_SIGNED in line: print ' UID already signed' gpg.stdin.write('quit\n') return False elif not uids_signer.GPG_CONFIRM in line: print ' ERROR: GnuPG reported an unknown error' gpg.stdin.write('quit\n') return False # else its uids_signer.GPG_CONFIRM... debug('Confirming signing') gpg.stdin.write('Y\n') self.gpg_wait_for_string(gpg.stdout, uids_signer.GPG_ACK) # # gpg-agent doesn't always work as well as we like. Of the problems: # * It can't always pop up an X window reliably (pinentry problems) # * It doesn't seem able to figure out the best pinetry program # to use in many situations # * Sometimes it silently fails in odd ways # # So this chunk of code will follow gpg through as many tries as gpg-agent # is willing to give and then inform the user of an error and raise an # exception. # # Since we're here, we also handle the highly unlikely case where the # verified cached passphrase doesn't work. # while True: line = gpg.stdout.readline() debug('Got %s' % line) if 'BAD_PASSPHRASE' in line: if self.mode == MODE_AGENT: line = gpg.stdout.readline() debug('Got %s' % line) if 'USERID_HINT' in line: continue print ' ERROR: Agent didn\'t provide passphrase to PGP.' raise AgentError else: print ' ERROR: GPG didn\'t accept the passphrase.' raise PassphraseError if 'GOOD_PASSPHRASE' in line: break debug('Saving key') self.gpg_wait_for_string(gpg.stdout, uids_signer.GPG_PROMPT) gpg.stdin.write('save\n') gpg.wait() return True def print_filenames(self, uids): '''Print the filenames we created for the user.''' print ' Signed UNencrypted keys: ' for index in range (1, len(uids)): if uids[index]['status'] != 'r' and uids[index]['result']: print ' %(id)s: %(file)s' % uids[index] if self.encrypt_outfiles: print ' Signed encrypted keys: ' for index in range (1, len(uids)): if uids[index]['status'] != 'r' and uids[index]['result']: print ' %(id)s: %(enc_file)s' % uids[index] def sign_all_uids(self, key): '''The main function that signs all the UIDs on a given key.''' uids = self.get_uids(key) print ' There are %s UIDs on this key to sign' % (len(uids) - 1) # From the user key ring make a clean copy self.export_clean_key(key) for index in range(1, len(uids)): if uids[index]['status'] == 'r': print ' Skipping revoked uid %s' % index continue sys.stdout.write(' UID %s (%s): ' % (index, uids[index]['id'])) # Make sure we have a clean keyring, and then import the key we care # about self.nuke_working_keyring() self.import_clean_key(key) if self.mode in (MODE_CACHE_PASSPHRASE, MODE_AGENT): try: res = self.sign_uid(key, index) except AgentError: print 'gpg-agent problems, bailing out!' sys.exit(1) except PassphraseError: print ('The passphrase that worked a moment ago now doesn\'t work.' ' I\'m bailing out!') sys.exit(1) else: res = self.sign_uid_expect(key, index) if not res: uids[index]['result'] = False continue sys.stdout.write('signed') uids[index]['result'] = True self.export_signed_uid(key, uids[index]['file']) if self.encrypt_outfiles: try: uids[index]['enc_file'] = self.encrypt_signed_uid(key, uids[index]['file']) except EncryptionKeyError: print ('Encryption failed due to invalid key error. User may not' ' have an encryption subkey.') uids[index]['enc_file'] = None sys.stdout.write(', encrypted') if self.mail: if uids[index]['email'] == None: print (' WARNING: No email for %s, cannot send key.' % uids[index]['id']) continue self.send_mail(uids[index]['email'], key, uids[index]['enc_file']) sys.stdout.write(', mailed') # add a newline to all the sys.stdout.write()s print '' if self.verbose: self.print_filenames(uids) # Remove the clean keyfile we temporarily created self.clean_clean_key(key) def import_unsigned_keys(self): '''Import all the unsigned keys from keyring to main keyring.''' print 'Importing keyring...' cmd = ('%s %s --import %s' % (self.gpg, self.gpg_quiet_opts, self.keyring)) gpg = os.popen(cmd, 'r') gpg.close() def send_mail(self, email, key, filename): '''Send the encrypted uid off to the user.''' # Testing msg = MIMEMultipart.MIMEMultipart() msg['From'] = self.mail msg['To'] = email msg['Date'] = formatdate(localtime=True) msg['Subject'] = 'Your signed PGP key' msg.epilogue = '' # Note the line with th eemail address on it below is intentionally # shorter than the rest to give it space to grow and still be < 80. if self.mail_text: part = MIMEText.MIMEText(open(self.mail_text, 'r').read()) else: part = MIMEText.MIMEText(DEFAULT_EMAIL_TEXT % (key, self.signer, email)) msg.attach(part) part = MIMEBase.MIMEBase('application', 'octet-stream') part.add_header('Content-Disposition','inline; filename="msg.asc"') part.set_payload(open(filename, 'r').read()) msg.attach(part) smtp = smtplib.SMTP('localhost') if self.override_email: env_to = self.override_email else: # BCC the user... env_to = [email, self.mail] smtp.sendmail(self.mail, env_to, msg.as_string()) smtp.close() # END class uids_signer def main(): global debug_on usage = ('%prog [options] -s [ ...]\n' ' %prog [options] -A -r -s ') parser = OptionParser(usage=usage, version='%%prog %s' % VERSION) parser.set_defaults(gpg_path=DEFAULT_GPG_PATH, out_dir=DEFAULT_OUT_DIR, tmp_dir=DEFAULT_TMP_DIR, keyring=DEFAULT_KEYRING, sign_level='3') parser.add_option('-a', '--use-agent', action='store_true', dest='agent', help='Use pgp-agent instead of letting gpg prompt the' ' user for every UID.') parser.add_option('-A', '--all-keys', action='store_true', dest='all_keys', help='Sign all keys on the keyring. Requires -r.') parser.add_option('-b', '--gpg-path', dest='gpg_path', metavar='PATH', help='Path to gpg binary. [default: %default]') parser.add_option('-e', '--encrypt-outfiles', action='store_true', dest='encrypt_outfiles', help='Encrypt output files with respective keys.') parser.add_option('-d', '--debug', action='store_true', dest='debug', help='Enable debugging output.') parser.add_option('-i', '--import', action='store_true', dest='import_keyring', help='Also import the unsigned keys from the keyring' ' into the default keyring. Ignored if -r is not' ' specified, or if it\'s the same as the default' ' keyring.') parser.add_option('-l', '--signature-level', dest='sign_level', metavar='LEVEL', help='The level to sign keys at (0-3).') parser.add_option('-m', '--mail', dest='mail', metavar='EMAIL', help='Email the encrypted, signed keys to the' ' respective email addresses. EMAIL is the address' ' to send from.') parser.add_option('-M', '--mail-text', dest='mail_text', metavar='FILE', help='Use the text in FILE as the body of email when' ' sending out emails instead of the default text.' ' To see the default text use' ' --print-default-email. Requires -m.') parser.add_option('-n', '--override-email', dest='override_email', metavar='EMAIL', help='Rather than send to the user, send to this address.' ' Mostly useful for debugging.') parser.add_option('-o', '--out-dir', dest='out_dir', metavar='OUTDIR', help='Directory to put signed keys in. [default: %default]') parser.add_option('-p', '--cache_passphrase', action='store_true', dest='cache_passphrase', help='Cache private key passphrase in memory and provide' ' it to gpg instead of letting gpg prompt the user' ' for every UID.') parser.add_option('-r', '--keyring', dest='keyring', metavar='KEYRING', help='The keyring to use. [default: %default]') parser.add_option('-s', '--signer', dest='signer', help='The keyid to sign with (required).') parser.add_option('-t', '--tmp-dir', dest='tmp_dir', help='Directory to put temporary stuff in. [default:' ' %default]') parser.add_option('-T', '--print-default-email', dest='print_default_email', action='store_true', help='Print the default email.') parser.add_option('-v', '--verbose', dest='verbose', action='store_true', help='Be more verbose.') (options, args) = parser.parse_args() print 'Welcome to PIUS, the PGP Individual UID Signer.\n' if options.print_default_email: print_default_email() sys.exit(0) if options.debug == True: print 'Setting debug' debug_on = True; if not options.signer: print 'You must specify a keyid to sign with.' sys.exit(1) if options.cache_passphrase and options.agent: print 'You can only select one of -a or -p' sys.exit(1) if options.keyring: options.keyring = os.path.expanduser(options.keyring) if not os.path.exists(options.keyring): print 'Keyring %s doesn\'t exist' % options.keyring sys.exit(1) mode = MODE_INTERACTIVE if options.cache_passphrase: mode = MODE_CACHE_PASSPHRASE elif options.agent: mode = MODE_AGENT if not options.all_keys: if not args: print 'Keyid required' sys.exit(1) else: if not options.keyring: print 'The -A options requires the -r option' sys.exit(1) if options.mail and not options.encrypt_outfiles: print 'WARNING: Turning on -e due to -m' options.encrypt_outfiles = True if options.mail_text and not options.mail: print 'ERROR: -M requires -m' sys.exit(1) for dir in (options.tmp_dir, options.out_dir): if not os.path.exists(dir): os.mkdir(dir, 0700) p = uids_signer(options.signer, mode, options.keyring, options.gpg_path, options.tmp_dir, options.out_dir, options.encrypt_outfiles, options.sign_level, options.mail, options.verbose, options.mail_text, options.override_email) if options.all_keys: list = p.get_all_keyids() if args: list.extend(args) else: list = args if options.import_keyring: if ((not options.keyring) or (options.keyring == DEFAULT_KEYRING)): print ('WARNING: Ignoring -i: Either -r wasn\'t specified, or it was' ' was the same as the default keyring.') else: p.import_unsigned_keys() if options.cache_passphrase: print ('WARNING: Using the -p option may have negative security' ' implications!') while not p.passphrase_confirmed: p.get_passphrase() if not p.verify_passphrase(): print 'Sorry, cannot unlock the key with that passphrase, try again.' for key in list: if not p.check_fingerprint(key): continue print 'Signing all UIDs on key %s' % key p.sign_all_uids(key) p.cleanup() main()