def __init__(self): super(KeyDetailsPage, self).__init__() self.set_spacing(10) self.log = logging.getLogger() # FIXME: this should be moved to KeySignSection self.keyring = Keyring() uidsLabel = Gtk.Label() uidsLabel.set_text("UIDs") # this will later be populated with uids when user selects a key self.uidsBox = Gtk.VBox(spacing=5) self.expireLabel = Gtk.Label() self.expireLabel.set_text("Expires 0000-00-00") self.signatures_label = signaturesLabel = Gtk.Label() signaturesLabel.set_text("Signatures") # this will also be populated later self.signaturesBox = Gtk.VBox(spacing=5) self.pack_start(uidsLabel, False, False, 0) self.pack_start(self.uidsBox, True, True, 0) self.pack_start(self.expireLabel, False, False, 0) self.pack_start(signaturesLabel, False, False, 0) self.pack_start(self.signaturesBox, True, True, 0)
def get_usable_secret_keys(pattern="", homedir=None): '''Returns all secret keys which can be used to sign a key''' keyring = Keyring(homedir=homedir) return get_usable_keys_from_keyring(keyring=keyring, pattern=pattern, public=False, secret=True)
def get_usable_keys(pattern="", homedir=None): '''Uses get_keys on the keyring and filters for non revoked, expired, disabled, or invalid keys''' log.debug('Retrieving keys for %s, %s', pattern, homedir) keyring = Keyring(homedir=homedir) return get_usable_keys_from_keyring(keyring=keyring, pattern=pattern, public=True, secret=False)
def get_public_key_data(fpr, keyring=None): """Returns keydata for a given fingerprint In fact, fpr could be anything that gpg happily exports. """ if not keyring: keyring = Keyring() keydata = keyring.export_data(fpr) return keydata
def get_public_key_data(fpr, homedir=None): """Returns keydata for a given fingerprint In fact, fpr could be anything that gpg happily exports. """ keyring = Keyring(homedir=homedir) keydata = keyring.export_data(fpr) if not keydata: s = "No data to export for {} (in {})".format(fpr, homedir) raise ValueError(s) return keydata
def __init__(self, base_keyring=None, *args, **kwargs): # Not a new style class... if issubclass(self.__class__, object): super(TempSigningKeyring, self).__init__(*args, **kwargs) else: TempKeyring.__init__(self, *args, **kwargs) if base_keyring is None: base_keyring = Keyring() # Copy the public parts of the secret keys to the tmpkeyring for fpr, key in base_keyring.get_keys(None, secret=True, public=False).items(): self.import_data(base_keyring.export_data(fpr))
def main(): k = Keyring() secret_keys = k.get_keys(public=False, secret=True) log.debug('Secret Keys: %s', secret_keys) for fpr, key in secret_keys.items(): # Because gnupg does not list whether the key has been revoked # when listing secret keys, we need to list public keys pub_keys = k.get_keys(fpr, public=True) assert len(pub_keys) == 1 pub_key = pub_keys[fpr] revoked = pub_key.revoked log.debug("Key %s is revoked: %r", pub_key, revoked)
def main(): k = Keyring() secret_keys = k.get_keys(public=False, secret=True) log.debug('Secret Keys: %s', secret_keys) for fpr, key in secret_keys.items(): log.debug("Keys: %s", key) exp = key.expiry log.debug("Key's expiry: %r", exp) if exp: expiry = datetime.fromtimestamp(int(exp)) now = datetime.now() expired = now > expiry else: expired = False log.debug("Key's expired: %s - %s", key.expired, expired)
def __init__(self): '''Initialises the section which lets the user choose a key to be signed by other person. ''' super(KeySignSection, self).__init__() self.log = logging.getLogger(__name__) self.keyring = Keyring() # these are needed later when we need to get details about # a selected key self.keysPage = KeysPage() self.keysPage.connect('key-selection-changed', self.on_key_selection_changed) self.keysPage.connect('key-selected', self.on_key_selected) self.keyDetailsPage = KeyDetailsPage() self.keyPresentPage = KeyPresentPage() # create back button self.backButton = Gtk.Button('Back') self.backButton.set_image( Gtk.Image.new_from_icon_name("go-previous", Gtk.IconSize.BUTTON)) self.backButton.set_always_show_image(True) self.backButton.connect('clicked', self.on_button_clicked) # set up notebook container self.notebook = Gtk.Notebook() self.notebook.append_page(self.keysPage, None) vbox = Gtk.VBox() # We place the button at the top, but that might not be the # smartest thing to do. Feel free to rearrange # FIXME: Consider a GtkHeaderBar for the application vbox.pack_start(self.backButton, False, False, 0) vbox.pack_start(self.keyPresentPage, True, True, 10) self.notebook.append_page(vbox, None) self.notebook.set_show_tabs(False) self.pack_start(self.notebook, True, True, 0) # this will hold a reference to the last key selected self.last_selected_key = None # When obtaining a key is successful, # it will save the key data in this field self.received_key_data = None self.keyserver = None
def signatures_for_keyid(keyid, keyring=None): '''Returns the list of signatures for a given key id This will call out to GnuPG list-sigs, using Monkeysign, and parse the resulting string into a list of signatures. A default Keyring will be used unless you pass an instance as keyring argument. ''' if keyring is None: kr = Keyring() else: kr = keyring # FIXME: this would be better if it was done in monkeysign kr.context.call_command(['list-sigs', keyid]) siglist = parse_sig_list(kr.context.stdout) return siglist
def get_usable_secret_keys(keyring=None, pattern=None): '''Returns all secret keys which can be used to sign a key Uses get_keys on the keyring and filters for non revoked, expired, disabled, or invalid keys''' if keyring is None: keyring = Keyring() secret_keys_dict = keyring.get_keys(pattern=pattern, public=False, secret=True) secret_key_fprs = secret_keys_dict.keys() log.debug('Detected secret keys: %s', secret_key_fprs) usable_keys_fprs = filter( lambda fpr: get_usable_keys(keyring, pattern=fpr, public=True), secret_key_fprs) usable_keys = [ Key.from_monkeysign(secret_keys_dict[fpr]) for fpr in usable_keys_fprs ] log.info('Returning usable private keys: %s', usable_keys) return usable_keys
def main(): import sys key = sys.argv[1] keyring = Keyring() keys = keyring.get_keys(key) # Heh, we take the first key here. Maybe we should raise a warning # or so, when there is more than one key. key = next(iter(keys.items()))[1] fpr = key.fpr data = 'OPENPGP4FPR:' + fpr w = Gtk.Window() w.connect("delete-event", Gtk.main_quit) w.set_default_size(100, 100) v = Gtk.VBox() label = Gtk.Label(data) qr = QRImage(data) v.add(label) v.add(qr) w.add(v) w.show_all() Gtk.main()
def get_usable_keys(keyring=None, *args, **kwargs): '''Uses get_keys on the keyring and filters for non revoked, expired, disabled, or invalid keys''' log.debug('Retrieving keys for %s, %s', args, kwargs) if keyring is None: keyring = Keyring() keys_dict = keyring.get_keys(*args, **kwargs) assert keys_dict is not None, keyring.context.stderr def is_usable(key): unusable = key.invalid or key.disabled \ or key.expired or key.revoked log.debug('Key %s is invalid: %s (i:%s, d:%s, e:%s, r:%s)', key, unusable, key.invalid, key.disabled, key.expired, key.revoked) return not unusable # keys_fpr = keys_dict.items() keys = keys_dict.values() usable_keys = [Key.from_monkeysign(key) for key in keys if is_usable(key)] log.debug('Identified usable keys: %s', usable_keys) return usable_keys
def test_sign_and_encrypt(self): # Let's imagine we've just got sent the key from the key-sending side keydata = open(self.sender_key, "rb").read() # for some reason pgpy does not like the stray data before # https://github.com/SecurityInnovation/PGPy/issues/218 keydata = keydata[keydata.index('-----'):] # We find out what UIDs the sender key has keys = get_usable_keys(homedir=self.sender_homedir) assert_equals(1, len(keys)) key = keys[0] uids = key.uidslist del key del keys # We are the receiver and we sign the sender's key. # This is a tuple (uid, encrypted) uid_encrypted = list( sign_keydata_and_encrypt(keydata, error_cb=None, homedir=self.receiver_homedir)) assert_equals(len(uids), len(uid_encrypted)) signatures_before = {} signatures_after = {} import pgpy pgpykeys = pgpy.PGPKey.from_blob(keydata) log.info("Loaded Keys: %r", pgpykeys) k = pgpykeys[0] for uid in k.userids: # We make the UID a string, because we might not get the # very same object back later. And I don't know whether # the dict uses "is" or "eq" for finding members signatures_before[u"{}".format(uid)] = uid._signatures for plain_uid, enc_uid in zip(uids, uid_encrypted): uid_from_signing = enc_uid[0] signed_uid = enc_uid[1] # The test doesn't work so well, because comments # are not rendered :-/ # assert_in(uid.uid, [e[0] for e in uid_encrypted]) # Decrypt... from monkeysign.gpg import Keyring # We sent back the key to the key-sending side kr = Keyring(homedir=self.sender_homedir) log.info("encrypted UID: %r", enc_uid) decrypted = kr.decrypt_data(signed_uid) log.info("ctx out: %r", kr.context.stdout) log.info("ctx err: %r", kr.context.stderr) assert_true(decrypted, "Error decrypting %r" % signed_uid) # Now we have the signed UID. We want see if it really carries a signature. pgpykeys = pgpy.PGPKey.from_blob(decrypted) log.info("Loaded Signed Keys: %r", pgpykeys) k = pgpykeys[0] # assert_equal(uid_from_signing, k.userids[0]) assert_equal(len(k.userids), 1) uid = k.userids[0] uidstr = u"{}".format(uid) assert_in(uidstr, signatures_before) # Now we have the signed UID. We want see if it really carries a signature. signatures_after[uidstr] = uid._signatures assert_less(len(signatures_before[uidstr]), len(signatures_after[uidstr]))
def sign_key_async(self, fingerprint=None, callback=None, data=None, error_cb=None): self.log.debug("I will sign key with fpr {}".format(fingerprint)) keyring = Keyring() keyring.context.set_option('export-options', 'export-minimal') tmpkeyring = TempSigningKeyring(keyring) # Eventually, we want to let the user select their keys to sign with # For now, we just take whatever is there. secret_keys = get_usable_secret_keys(tmpkeyring) self.log.info('Signing with these keys: %s', secret_keys) keydata = data or self.received_key_data if keydata: stripped_key = MinimalExport(keydata) fpr = fingerprint_for_key(stripped_key) if fingerprint is None: # The user hasn't provided any data to operate on fingerprint = fpr if not fingerprint == fpr: self.log.warning('Something strange is going on. ' 'We wanted to sign fingerprint "%s", received ' 'keydata to operate on, but the key has fpr "%s".', fingerprint, fpr) else: # Do we need this branch at all? if fingerprint is None: raise ValueError('You need to provide either keydata or a fpr') self.log.debug("looking for key %s in your keyring", fingerprint) keyring.context.set_option('export-options', 'export-minimal') stripped_key = keyring.export_data(fingerprint) self.log.debug('Trying to import key\n%s', stripped_key) if tmpkeyring.import_data(stripped_key): # 3. for every user id (or all, if -a is specified) # 3.1. sign the uid, using gpg-agent keys = tmpkeyring.get_keys(fingerprint) self.log.info("Found keys %s for fp %s", keys, fingerprint) assert len(keys) == 1, "We received multiple keys for fp %s: %s" % (fingerprint, keys) key = keys[fingerprint] uidlist = key.uidslist for secret_key in secret_keys: secret_fpr = secret_key.fpr self.log.info('Setting up to sign with %s', secret_fpr) # We need to --always-trust, because GnuPG would print # warning about the trustdb. I think this is because # we have a newly signed key whose trust GnuPG wants to # incorporate into the trust decision. tmpkeyring.context.set_option('always-trust') tmpkeyring.context.set_option('local-user', secret_fpr) # FIXME: For now, we sign all UIDs. This is bad. ret = tmpkeyring.sign_key(uidlist[0].uid, signall=True) self.log.info("Result of signing %s on key %s: %s", uidlist[0].uid, fingerprint, ret) for uid in uidlist: uid_str = uid.uid self.log.info("Processing uid %s %s", uid, uid_str) # 3.2. export and encrypt the signature # 3.3. mail the key to the user signed_key = UIDExport(uid_str, tmpkeyring.export_data(uid_str)) self.log.info("Exported %d bytes of signed key", len(signed_key)) # self.signui.tmpkeyring.context.set_option('armor') tmpkeyring.context.set_option('always-trust') encrypted_key = tmpkeyring.encrypt_data(data=signed_key, recipient=uid_str) keyid = str(key.keyid()) ctx = { 'uid' : uid_str, 'fingerprint': fingerprint, 'keyid': keyid, } # We could try to dir=tmpkeyring.dir # We do not use the with ... as construct as the # tempfile might be deleted before the MUA had the chance # to get hold of it. # Hence we reference the tmpfile and hope that it will be properly # cleaned up when this object will be destroyed... tmpfile = NamedTemporaryFile(prefix='gnome-keysign-', suffix='.asc') self.tmpfiles.append(tmpfile) filename = tmpfile.name self.log.info('Writing keydata to %s', filename) tmpfile.write(encrypted_key) # Interesting, sometimes it would not write the whole thing out, # so we better flush here tmpfile.flush() # As we're done with the file, we close it. #tmpfile.close() subject = Template(SUBJECT).safe_substitute(ctx) body = Template(BODY).safe_substitute(ctx) self.email_file (to=uid_str, subject=subject, body=body, files=[filename]) # FIXME: Can we get rid of self.tmpfiles here already? Even if the MUA is still running? # 3.4. optionnally (-l), create a local signature and import in # local keyring # 4. trash the temporary keyring else: self.log.error('data found in barcode does not match a OpenPGP fingerprint pattern: %s', fingerprint) if error_cb: GLib.idle_add(error_cb, data) return False
def __init__(self, show_public_keys=False): '''Sets the widget up. The show_public_keys parameter is meant for development purposes only. If set to True, the widget will show the public keys, too. Otherwise, secret keys are shown. ''' super(KeysPage, self).__init__() # set up the list store to be filled up with user's gpg keys # Note that other functions expect a certain structure to # this ListStore, e.g. when parsing the selection of the # TreeView, i.e. in get_items_from_selection. self.store = Gtk.ListStore(str, str, str) # FIXME: this should be moved to KeySignSection self.keyring = Keyring() # the user's keyring self.keysDict = {} # FIXME: this should be a callback function to update the display # when a key is changed/deleted keys = self.keyring.get_keys(None, secret=True, public=show_public_keys) for fpr, key in keys.items(): if key.invalid or key.disabled or key.expired or key.revoked: continue uidslist = key.uidslist #UIDs: Real Name (Comment) <email@address> keyid = str(key.keyid()) # the key's short id if not keyid in self.keysDict: self.keysDict[keyid] = key for e in uidslist: uid = str(e.uid) # remove the comment from UID (if it exists) com_start = uid.find('(') if com_start != -1: com_end = uid.find(')') uid = uid[:com_start].strip() + uid[com_end + 1:].strip() # split into user's name and email tokens = uid.split('<') name = tokens[0].strip() email = 'unknown' if len(tokens) > 1: email = tokens[1].replace('>', '').strip() self.store.append((name, email, keyid)) if len(self.store) == 0: self.pack_start(Gtk.Label("You don't have a private key"), True, True, 0) else: # create the tree view self.treeView = Gtk.TreeView(model=self.store) # setup 'Name' column nameRenderer = Gtk.CellRendererText() nameColumn = Gtk.TreeViewColumn("Name", nameRenderer, text=0) # setup 'Email' column emailRenderer = Gtk.CellRendererText() emailColumn = Gtk.TreeViewColumn("Email", emailRenderer, text=1) # setup 'Key' column keyRenderer = Gtk.CellRendererText() keyColumn = Gtk.TreeViewColumn("Key", keyRenderer, text=2) self.treeView.append_column(nameColumn) self.treeView.append_column(emailColumn) self.treeView.append_column(keyColumn) self.treeView.connect('row-activated', self.on_row_activated) # make the tree view resposive to single click selection self.treeView.get_selection().connect('changed', self.on_selection_changed) # make the tree view scrollable self.scrolled_window = Gtk.ScrolledWindow() self.scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.scrolled_window.add(self.treeView) self.scrolled_window.set_min_content_height(200) #self.pack_start(self.scrolled_window, True, True, 0) self.hpane = Gtk.HPaned() self.hpane.pack1(self.scrolled_window, False, False) self.right_pane = Gtk.VBox() right_label = Gtk.Label(label='Select key on the left') self.right_pane.add(right_label) # Hm, right now, the width of the right pane changes, when # a key is selected, because the right pane's content will be # wider when it displays expiration et al. # Can we hint at that fact and make the VBox a bit wider than necessary? #padded_label = Gtk.Label(label='Select key on the left'*3) #self.right_pane.add(padded_label) self.hpane.pack2(self.right_pane, True, False) self.pack_start(self.hpane, True, True, 0)
def sign_keydata(keydata, error_cb=None, homedir=None): """Signs OpenPGP keydata with your regular GnuPG secret keys If error_cb is provided, that function is called with any exception occuring during signing of the key. If error_cb is False, any exception is raised. yields pairs of (uid, signed_uid) """ log = logging.getLogger(__name__ + ':sign_keydata_encrypt') tmpkeyring = TempSigningKeyring(homedir=homedir, base_keyring=Keyring(homedir=homedir)) tmpkeyring.context.set_option('export-options', 'export-minimal') # Eventually, we want to let the user select their keys to sign with # For now, we just take whatever is there. secret_keys = get_usable_keys_from_keyring(keyring=tmpkeyring, pattern="", public=False, secret=True) log.info('Signing with these keys: %s', secret_keys) stripped_key = MinimalExport(keydata) fingerprint = fingerprint_from_keydata(stripped_key) log.debug('Trying to import key\n%s', stripped_key) if tmpkeyring.import_data(stripped_key): # 3. for every user id (or all, if -a is specified) # 3.1. sign the uid, using gpg-agent keys = tmpkeyring.get_keys(fingerprint) log.info("Found keys %s for fp %s", keys, fingerprint) if len(keys) != 1: raise ValueError("We received multiple keys for fp %s: %s" % (fingerprint, keys)) key = keys[fingerprint] uidlist = key.uidslist for secret_key in secret_keys: secret_fpr = secret_key.fpr log.info('Setting up to sign with %s', secret_fpr) # We need to --always-trust, because GnuPG would print # warning about the trustdb. I think this is because # we have a newly signed key whose trust GnuPG wants to # incorporate into the trust decision. tmpkeyring.context.set_option('always-trust') tmpkeyring.context.set_option('local-user', secret_fpr) # FIXME: For now, we sign all UIDs. This is bad. try: ret = tmpkeyring.sign_key(fingerprint, signall=True) except GpgRuntimeError as e: uid = uidlist[0].uid log.exception( "Error signing %r with secret key %r. stdout: %r, stderr: %r", uid, secret_key, tmpkeyring.context.stdout, tmpkeyring.context.stderr) if error_cb: e.uid = uid error_cb(e) else: raise continue log.info("Result of signing %s on key %s: %s", uidlist[0].uid, fingerprint, ret) for uid in uidlist: uid_str = uid.uid log.info("Processing uid %r %s", uid, uid_str) # 3.2. export and encrypt the signature # 3.3. mail the key to the user signed_key = UIDExport(uid_str, tmpkeyring.export_data(uid_str)) log.info("Exported %d bytes of signed key", len(signed_key)) yield (uid, signed_key)
def get_public_key_data(fpr): keydata = Keyring().export_data(fpr) return keydata