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 __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 __init__(self, base_keyring=None, *args, **kwargs): # Not a new style class... if issubclass(self.__class__, object): super(TempSigningKeyring, self).__init__(*args, **kwargs) else: TempSplitKeyring.__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(): 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 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. fpr = keys.items()[0][0] 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 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
class KeysPage(Gtk.VBox): def __init__(self, keySection): super(KeysPage, self).__init__() # pass a reference to KeySignSection in order to access its widgets self.keySection = keySection # set up the list store to be filled up with user's gpg keys 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 for key in self.keyring.get_keys(None, True, False).values(): 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)) # 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) # 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) def on_selection_changed(self, *args): self.keySection.nextButton.set_sensitive(True)
class KeysPage(Gtk.VBox): '''This represents a list of keys with the option for the user to select one key to proceed. This class emits a `key-selection-changed' signal when the user initially selects a key such that it is highlighted. The `key-selected' signal is emitted when the user commits to a key, i.e. by pressing a designated button to make his selection public. ''' __gsignals__ = { str('key-selected'): (GObject.SIGNAL_RUN_LAST, None, # Hm, this is a str for now, but ideally # it'd be the full key object (str,)), str('key-selection-changed'): (GObject.SIGNAL_RUN_LAST, None, # Hm, this is a str for now, but ideally # it'd be the full key object (str,)), } 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) # We could make it a @staticmethod, but the returned items # are bound to the model, anyway. So it probably doesn't # make much sense to have a static function, anyway. def get_items_from_selection(self, selection=None): '''Returns the elements in the ListStore for the given selection''' s = selection or self.treeView.get_selection() model, paths = s.get_selected_rows() name = email = keyid = None for path in paths: iterator = model.get_iter(path) (name, email, keyid) = model.get(iterator, 0, 1, 2) break return (name, email, keyid) def on_selection_changed(self, selection, *args): log.debug('Selected new TreeView item %s = %s', selection, args) name, email, keyid = self.get_items_from_selection(selection) key = self.keysDict[keyid] self.emit('key-selection-changed', keyid) try: exp_date = datetime.fromtimestamp(float(key.expiry)) except TypeError as e: # This might be the case when the key.expiry is already a timedate exp_date = key.expiry except ValueError as e: # This happens when converting an empty string to a datetime. exp_date = None if exp_date is None: expiry = "No expiration date" else: expiry = "{:%Y-%m-%d %H:%M:%S}".format(exp_date) pane = self.right_pane for child in pane.get_children(): # Ouch, this is not very efficient. # But this deals with the fact that the first # label in the pane is a "Select a key on the left" # text. pane.remove(child) ctx = {'keyid':keyid, 'expiry':expiry, 'sigs':''} keyid_label = Gtk.Label(label='Key {keyid}'.format(**ctx)) expiration_label = Gtk.Label(label='Expires: {expiry}'.format(**ctx)) #signatures_label = Gtk.Label(label='{sigs} signatures'.format(**ctx)) publish_button = Gtk.Button(label='Go ahead!'.format(**ctx)) publish_button.connect('clicked', self.on_publish_button_clicked, key) for w in (keyid_label , expiration_label #, signatures_label , publish_button ): pane.add(w) pane.show_all() def on_row_activated(self, treeview, tree_path, column): '''A callback for when the user "activated" a row, e.g. by double-clicking an entry. It emits the key-selected signal. ''' # We just hijack the existing function. # I'm sure we could get the required information out of # the tree_path and column, but I don't know how. name, email, keyid = self.get_items_from_selection() self.emit('key-selected', keyid) def on_publish_button_clicked(self, button, key, *args): '''Callback for when the user has expressed their wish to publish a key on the network. It will emit a "key-selected" signal with the ID of the selected key.''' log.debug('Clicked publish for key (%s) %s (%s)', type(key), key, args) keyid = key.keyid() self.emit('key-selected', keyid)
class KeySignSection(Gtk.VBox): 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 on_key_selection_changed(self, pane, keyid): '''This callback is attached to the signal which is emitted when the user changes their selection in the list of keys ''' pass def on_key_selected(self, pane, keyid): '''This is the callback for when the user has committed to a key, i.e. the user has made a selection and wants to advance the program. ''' log.debug('User selected key %s', keyid) key = list(self.keyring.get_keys(keyid).values())[0] keyid = key.keyid() fpr = key.fpr self.keyring.export_data(fpr, secret=False) keydata = self.keyring.context.stdout self.log.debug("Keyserver switched on! Serving key with fpr: %s", fpr) self.setup_server(keydata, fpr) self.switch_to_key_present_page(key) def switch_to_key_present_page(self, key): '''This switches the notebook to the page which presents the information that is needed to securely transfer the keydata, i.e. the fingerprint and its barcode. ''' self.keyPresentPage.display_fingerprint_qr_page(key) self.notebook.next_page() # This is more of a crude hack. Once the next page is presented, # the back button has the focus. This is not desirable because # you will go back when accidentally pressing space or enter. self.keyPresentPage.fingerprintLabel.grab_focus() # FIXME: we better use set_current_page, but that requires # knowing which page our desired widget is on. # FWIW: A headerbar has named pages. def on_next_button_clicked(self, button): '''A helper for legacy reasons to enable a next button All it does is retrieve the selection from the TreeView and call the signal handler for when the user committed to a key ''' name, email, keyid = self.keysPage.get_items_from_selection() return self.on_key_selected(button, keyid) def on_button_clicked(self, button): page_index = self.notebook.get_current_page() if button == self.backButton: if page_index == 1: self.log.debug("Keyserver switched off") self.stop_server() self.notebook.prev_page() def setup_server(self, keydata, fingerprint): """ Starts the key-server which serves the provided keydata and announces the fingerprint as TXT record using Avahi """ self.log.info('Serving now') self.log.debug('About to call %r', Keyserver.ServeKeyThread) self.keyserver = Keyserver.ServeKeyThread(str(keydata), fingerprint) self.log.info('Starting thread %r', self.keyserver) self.keyserver.start() self.log.info('Finsihed serving') return False def stop_server(self): self.keyserver.shutdown()
class KeysPage(Gtk.VBox): '''This represents a list of keys with the option for the user to select one key to proceed. This class emits a `key-selection-changed' signal when the user initially selects a key such that it is highlighted. The `key-selected' signal is emitted when the user commits to a key, i.e. by pressing a designated button to make his selection public. ''' __gsignals__ = { str('key-selected'): ( GObject.SIGNAL_RUN_LAST, None, # Hm, this is a str for now, but ideally # it'd be the full key object ( str, )), str('key-selection-changed'): ( GObject.SIGNAL_RUN_LAST, None, # Hm, this is a str for now, but ideally # it'd be the full key object ( str, )), } 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) # We could make it a @staticmethod, but the returned items # are bound to the model, anyway. So it probably doesn't # make much sense to have a static function, anyway. def get_items_from_selection(self, selection=None): '''Returns the elements in the ListStore for the given selection''' s = selection or self.treeView.get_selection() model, paths = s.get_selected_rows() name = email = keyid = None for path in paths: iterator = model.get_iter(path) (name, email, keyid) = model.get(iterator, 0, 1, 2) break return (name, email, keyid) def on_selection_changed(self, selection, *args): log.debug('Selected new TreeView item %s = %s', selection, args) name, email, keyid = self.get_items_from_selection(selection) key = self.keysDict[keyid] self.emit('key-selection-changed', keyid) try: exp_date = datetime.fromtimestamp(float(key.expiry)) except TypeError as e: # This might be the case when the key.expiry is already a timedate exp_date = key.expiry except ValueError as e: # This happens when converting an empty string to a datetime. exp_date = None if exp_date is None: expiry = "No expiration date" else: expiry = "{:%Y-%m-%d %H:%M:%S}".format(exp_date) pane = self.right_pane for child in pane.get_children(): # Ouch, this is not very efficient. # But this deals with the fact that the first # label in the pane is a "Select a key on the left" # text. pane.remove(child) ctx = {'keyid': keyid, 'expiry': expiry, 'sigs': ''} keyid_label = Gtk.Label(label='Key {keyid}'.format(**ctx)) expiration_label = Gtk.Label(label='Expires: {expiry}'.format(**ctx)) #signatures_label = Gtk.Label(label='{sigs} signatures'.format(**ctx)) publish_button = Gtk.Button(label='Go ahead!'.format(**ctx)) publish_button.connect('clicked', self.on_publish_button_clicked, key) for w in ( keyid_label, expiration_label #, signatures_label , publish_button): pane.add(w) pane.show_all() def on_row_activated(self, treeview, tree_path, column): '''A callback for when the user "activated" a row, e.g. by double-clicking an entry. It emits the key-selected signal. ''' # We just hijack the existing function. # I'm sure we could get the required information out of # the tree_path and column, but I don't know how. name, email, keyid = self.get_items_from_selection() self.emit('key-selected', keyid) def on_publish_button_clicked(self, button, key, *args): '''Callback for when the user has expressed their wish to publish a key on the network. It will emit a "key-selected" signal with the ID of the selected key.''' log.debug('Clicked publish for key (%s) %s (%s)', type(key), key, args) keyid = key.keyid() self.emit('key-selected', keyid)