Moved trust_box.py to gtkui
[zeroinstall/solver.git] / zeroinstall / gtkui / trust_box.py
blob43847dc1af05d8130d230f91c1f7054003537b13
1 # Copyright (C) 2009, Thomas Leonard
2 # -*- coding: utf-8 -*-
3 # See the README file for details, or visit http://0install.net.
5 import gtk
6 from zeroinstall.injector.model import SafeException
7 from zeroinstall.injector import gpg, trust
8 from zeroinstall.support import tasks
9 from zeroinstall.gtkui import help_box, gtkutils
11 def frame(page, title, content, expand = False):
12 frame = gtk.Frame()
13 label = gtk.Label()
14 label.set_markup('<b>%s</b>' % title)
15 frame.set_label_widget(label)
16 frame.set_shadow_type(gtk.SHADOW_NONE)
17 if type(content) in (str, unicode):
18 content = gtk.Label(content)
19 content.set_alignment(0, 0.5)
20 content.set_selectable(True)
21 frame.add(content)
22 if hasattr(content, 'set_padding'):
23 content.set_padding(8, 4)
24 else:
25 content.set_border_width(8)
26 page.pack_start(frame, expand, True, 0)
28 def pretty_fp(fp):
29 s = fp[0:4]
30 for x in range(4, len(fp), 4):
31 s += ' ' + fp[x:x + 4]
32 return s
34 def left(text):
35 label = gtk.Label(text)
36 label.set_alignment(0, 0.5)
37 label.set_selectable(True)
38 return label
40 def get_hint(fingerprint):
41 hint_icon = gtk.Image()
42 hint_text = hints.get(fingerprint, None)
43 if hint_text:
44 hint_icon.set_from_stock(gtk.STOCK_YES, gtk.ICON_SIZE_BUTTON)
45 else:
46 hint_icon.set_from_stock(gtk.STOCK_DIALOG_WARNING, gtk.ICON_SIZE_BUTTON)
47 hint_text = 'Warning: Nothing known about this key!'
48 hint = left(hint_text)
49 hint.set_line_wrap(True)
50 hint_hbox = gtk.HBox(False, 4)
51 hint_hbox.pack_start(hint_icon, False, True, 0)
52 hint_hbox.pack_start(hint, True, True, 0)
53 hint_icon.set_alignment(0, 0)
54 return hint_hbox
56 class TrustBox(gtk.Dialog):
57 interface = None
58 sigs = None
59 iface_xml = None
60 valid_sigs = None
61 parent = None
62 closed = None
64 def __init__(self, interface, sigs, iface_xml, parent):
65 gtk.Dialog.__init__(self)
66 self.set_has_separator(False)
67 self.set_position(gtk.WIN_POS_CENTER)
68 self.set_transient_for(parent)
70 self.closed = tasks.Blocker("confirming keys with user")
72 domain = trust.domain_from_url(interface.uri)
73 assert domain
75 def destroy(box):
76 global _queue
77 assert _queue[0] is self
78 del _queue[0]
80 self.closed.trigger()
82 # Remove any queued boxes that are no longer required
83 def still_untrusted(box):
84 for sig in box.valid_sigs:
85 is_trusted = trust.trust_db.is_trusted(sig.fingerprint, domain)
86 if is_trusted:
87 return False
88 return True
89 if _queue:
90 next = _queue[0]
91 if still_untrusted(next):
92 next.show()
93 else:
94 next.trust_keys([], domain)
95 next.destroy() # Will trigger this again...
96 self.connect('destroy', destroy)
98 self.interface = interface
99 self.sigs = sigs
100 self.iface_xml = iface_xml
102 self.set_title('Confirm trust')
104 vbox = gtk.VBox(False, 4)
105 vbox.set_border_width(4)
106 self.vbox.pack_start(vbox, True, True, 0)
108 self.valid_sigs = [s for s in sigs if isinstance(s, gpg.ValidSig)]
109 if not self.valid_sigs:
110 raise SafeException('No valid signatures found on "%s". Signatures:%s' %
111 (interface.uri, ''.join(['\n- ' + str(s) for s in sigs])))
113 notebook = gtk.Notebook()
115 if len(self.valid_sigs) == 1:
116 notebook.set_show_tabs(False)
118 label = left('Checking: ' + interface.uri)
119 label.set_padding(4, 4)
120 vbox.pack_start(label, False, True, 0)
122 currently_trusted_keys = trust.trust_db.get_keys_for_domain(domain)
123 if currently_trusted_keys:
124 keys = [gpg.load_key(fingerprint) for fingerprint in currently_trusted_keys]
125 descriptions = ["%s\n(fingerprint: %s)" % (key.name, pretty_fp(key.fingerprint))
126 for key in keys]
127 else:
128 descriptions = ['None']
129 frame(vbox, 'Keys already approved for "%s"' % domain, '\n'.join(descriptions))
131 if len(self.valid_sigs) == 1:
132 label = left('This key signed the feed:')
133 else:
134 label = left('These keys signed the feed:')
136 label.set_padding(4, 4)
137 vbox.pack_start(label, False, True, 0)
139 vbox.pack_start(notebook, True, True, 0)
141 self.add_button(gtk.STOCK_HELP, gtk.RESPONSE_HELP)
142 self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
143 self.add_button(gtk.STOCK_ADD, gtk.RESPONSE_OK)
144 self.set_default_response(gtk.RESPONSE_OK)
146 trust_checkbox = {} # Sig -> CheckButton
147 def ok_sensitive():
148 trust_any = False
149 for toggle in trust_checkbox.values():
150 if toggle.get_active():
151 trust_any = True
152 break
153 self.set_response_sensitive(gtk.RESPONSE_OK, trust_any)
155 for sig in self.valid_sigs:
156 if hasattr(sig, 'get_details'):
157 name = '<unknown>'
158 details = sig.get_details()
159 for item in details:
160 if item[0] in ('pub', 'uid') and \
161 len(item) > 9:
162 name = item[9]
163 break
164 else:
165 name = None
166 page = gtk.VBox(False, 4)
167 page.set_border_width(8)
169 frame(page, 'Fingerprint', pretty_fp(sig.fingerprint))
171 if name is not None:
172 frame(page, 'Claimed identity', name)
174 frame(page, 'Unreliable hints database says', get_hint(sig.fingerprint))
176 already_trusted = trust.trust_db.get_trust_domains(sig.fingerprint)
177 if already_trusted:
178 frame(page, 'You already trust this key for these domains',
179 '\n'.join(already_trusted))
181 trust_checkbox[sig] = gtk.CheckButton('_Trust this key')
182 page.pack_start(trust_checkbox[sig], False, True, 0)
183 trust_checkbox[sig].connect('toggled', lambda t: ok_sensitive())
185 notebook.append_page(page, gtk.Label(name or 'Signature'))
187 ok_sensitive()
188 self.vbox.show_all()
190 def response(box, resp):
191 if resp == gtk.RESPONSE_HELP:
192 trust_help.display()
193 return
194 if resp == gtk.RESPONSE_OK:
195 self.trust_keys([sig for sig in trust_checkbox if trust_checkbox[sig].get_active()], domain)
196 self.destroy()
197 self.connect('response', response)
199 def trust_keys(self, sigs, domain):
200 assert domain
201 try:
202 for sig in sigs:
203 trust.trust_db.trust_key(sig.fingerprint, domain)
205 trust.trust_db.notify()
206 except Exception, ex:
207 gtkutils.show_message_box(self, str(ex), gtk.MESSAGE_ERROR)
208 if not isinstance(ex, SafeException):
209 raise
211 _queue = []
212 def confirm_trust(interface, sigs, iface_xml, parent):
213 """Display a dialog box asking the user to confirm that one of the
214 keys is trusted for this domain. If a trust box is already visible, this
215 one is queued until the existing one is closed.
216 @param interface: the feed being loaded
217 @type interface: L{model.Interface}
218 @param sigs: the signatures on the feed
219 @type sigs: [L{gpg.Signature}]
220 @param iface_xml: the downloaded (untrusted) XML document
221 @type iface_xml: str
223 box = TrustBox(interface, sigs, iface_xml, parent)
224 _queue.append(box)
225 if len(_queue) == 1:
226 _queue[0].show()
227 return box.closed
229 trust_help = help_box.HelpBox("Trust Help",
230 ('Overview', """
231 When you run a program, it typically has access to all your files and can generally do \
232 anything that you're allowed to do (delete files, send emails, etc). So it's important \
233 to make sure that you don't run anything malicious."""),
235 ('Digital signatures', """
236 Each software author creates a 'key-pair'; a 'public key' and a 'private key'. Without going \
237 into the maths, only something encrypted with the private key will decrypt with the public key.
239 So, when a programmer releases some software, they encrypt it with their private key (which no-one \
240 else has). When you download it, the injector checks that it decrypts using their public key, thus \
241 proving that it came from them and hasn't been tampered with."""),
243 ('Trust', """
244 After the injector has checked that the software hasn't been modified since it was signed with \
245 the private key, you still have the following problems:
247 1. Does the public key you have really belong to the author?
248 2. Even if the software really did come from that person, do you trust them?"""),
250 ('Key fingerprints', """
251 To confirm (1), you should compare the public key you have with the genuine one. To make this \
252 easier, the injector displays a 'fingerprint' for the key. Look in mailing list postings or some \
253 other source to check that the fingerprint is right (a different key will have a different \
254 fingerprint).
256 You're trying to protect against the situation where an attacker breaks into a web site \
257 and puts up malicious software, signed with the attacker's private key, and puts up the \
258 attacker's public key too. If you've downloaded this software before, you \
259 should be suspicious that you're being asked to confirm another key!"""),
261 ('Reputation', """
262 In general, most problems seem to come from malicous and otherwise-unknown people \
263 replacing software with modified versions, or creating new programs intended only to \
264 cause damage. So, check your programs are signed by a key with a good reputation!"""))
266 hints = {
267 '1DC295D11A3F910DA49D3839AA1A7812B40B0B6E' :
268 'Ken Hayber has been writing ROX applications since 2003. This key '
269 'was announced on the rox-users list on 5 Jun 2005.',
271 '4338D5420E0BAEB6B2E73530B66A4F24AB8B4B65' :
272 'Thomas Formella is experimenting with packaging programs for 0launch. This key '
273 'was announced on 11 Sep 2005 on the zero-install mailing list.',
275 '92429807C9853C0744A68B9AAE07828059A53CC1' :
276 'Thomas Leonard created Zero Install and ROX. This key is used to sign updates to the '
277 'injector; you should accept it.',
279 'DA9825AECAD089757CDABD8E07133F96CA74D8BA' :
280 'Thomas Leonard created Zero Install and ROX. This key is used to sign updates to the '
281 'injector; you should accept it. It was announced on the Zero Install mailing list '
282 'on 2009-05-31.',
284 '0597A2AFB6B372ACB97AC6E433B938C2E9D8826D' :
285 'Stephen Watson is a project admin for the ROX desktop, and has been involved with the '
286 'project since 2000. This key has been used for signing software since the 23 Jul 2005 '
287 'announcement on the zero-install mailing list.',
289 'F0A0CA2A8D8FCC123F5EC04CD8D59DC384AE988E' :
290 'Piero Ottuzzi is experimenting with packaging programs for 0launch. This key has been '
291 'known since a 16 Mar 2005 post to the zero-install mailing list. It was first used to '
292 'sign software in an announcement posted on 9 Aug 2005.',
294 'FC71DC3364367CE82F91472DDF32928893D894E9' :
295 'Niklas Höglund is experimenting with using Zero Install on the Nokia 770. This key has '
296 'been known since the announcement of 4 Apr 2006 on the zero-install mailing list.',
298 'B93AAE76C40A3222425A04FA0BDA706F2C21E592' : # expired
299 'Ilja Honkonen is experimenting with packaging software for Zero Install. This key '
300 'was announced on 2006-04-21 on the zero-install mailing list.',
302 '6AD4A9C482F1D3F537C0354FC8CC44742B11FF89' :
303 'Ilja Honkonen is experimenting with packaging software for Zero Install. This key '
304 'was announced on 2009-06-18 on the zero-install mailing list.',
306 '5D3D90FB4E6FE10C7F76E94DEE6BC26DBFDE8022' :
307 'Dennis Tomas leads the rox4debian packaging effort. This key has been known since '
308 'an email forwarded to the rox-devel list on 2006-05-28.',
310 '2E2B4E59CAC8D874CD2759D34B1095AF2E992B19' :
311 'Lennon Cook creates the FreeBSD-x86 binaries for various ROX applications. '
312 'This key was announced in a Jun 17, 2006 post to the rox-devel mailing list.',
314 '7722DC5085B903FF176CCAA9695BA303C9839ABC' :
315 'Lennon Cook creates the FreeBSD-x86 binaries for various ROX applications. '
316 'This key was announced in an Oct 5, 2006 post to the rox-users mailing list.',
318 '03DC5771716A5A329CA97EA64AB8A8E7613A266F' :
319 'Lennon Cook creates the FreeBSD-x86 binaries for various ROX applications. '
320 'This key was announced in an Oct 7, 2007 post to the rox-users mailing list.',
322 '617794D7C3DFE0FFF572065C0529FDB71FB13910' :
323 'This low-security key is used to sign Zero Install interfaces which have been '
324 "automatically generated by a script. Typically, the upstream software didn't "
325 "come with a signature, so it's impossible to know if the code is actually OK. "
326 "However, there is still some benefit: if the archive is modified after the "
327 "script has signed it then any further changes will be detected, so this isn't "
328 "completely pointless.",
330 '5E665D0ECCCF1215F725BD2FA7421904E3D1B654' :
331 'Daniel Carrera works on the OpenDocument viewer from opendocumentfellowship.org. '
332 'This key was confirmed in a zero-install mailing list post on 2007-01-09.',
334 '635469E565B8D340C2C9EA4C32FBC18CE63EF486' :
335 'Eric Wasylishen is experimenting with packaging software with Zero Install. '
336 'This key was announced on the zero-install mailing list on 2007-01-16 and then lost.',
338 'E5175248514E9D4E558B5925BC456918F32AC5D1' :
339 'Eric Wasylishen is experimenting with packaging software with Zero Install. '
340 'This key was announced on the zero-install mailing list on 2008-12-07',
342 'C82D382AAB381A54529019D6A0F9B035686C6996' :
343 "Justus Winter is generating Zero Install feeds from pkgsrc (which was originally "
344 "NetBSD's ports collection). This key was announced on the zero-install mailing list "
345 "on 2007-06-01.",
347 'D7582A2283A01A6480780AC8E1839306AE83E7E2' :
348 'Tom Adams is experimenting with packaging software with Zero Install. '
349 'This key was announced on the zero-install mailing list on 2007-08-14.',
351 '3B2A89E694686DC4FEEFD6F6D00CA21EC004251B' :
352 'Tuomo Valkonen is the author of the Ion tiling window manager. This key fingerprint '
353 'was taken from http://modeemi.fi/~tuomov/ on 2007-11-17.',
355 'A14924F4DFD1B81DED3436240C9B2C41B8D66FEA' :
356 'Andreas K. Förster is experimenting with creating Zero Install feeds. '
357 'This key was announced in a 2008-01-25 post to the zeroinstall mailing list.',
359 '520DCCDBE5D38E2B22ADD82672E5E2ACF037FFC4' :
360 'Thierry Goubier creates PPC binaries for the ROX desktop. This key was '
361 'announced in a 2008-02-03 posting to the rox-users list.',
363 '517085B7261D3B03A97515319C2C2CD1D41AF5BB' :
364 'Frank Richter is a developer of the Crystal Space 3D SDK. This key was '
365 'confirmed in a 2008-09-04 post to the zero-install-devel mailing list.',