1 # Copyright (C) 2009, Thomas Leonard
2 # -*- coding: utf-8 -*-
3 # See the README file for details, or visit http://0install.net.
4 from zeroinstall
import _
7 from zeroinstall
.injector
.model
import SafeException
8 from zeroinstall
.injector
import gpg
, trust
9 from zeroinstall
.support
import tasks
10 from zeroinstall
.gtkui
import help_box
, gtkutils
12 def frame(page
, title
, content
, expand
= False):
15 label
.set_markup('<b>%s</b>' % title
)
16 frame
.set_label_widget(label
)
17 frame
.set_shadow_type(gtk
.SHADOW_NONE
)
18 if type(content
) in (str, unicode):
19 content
= gtk
.Label(content
)
20 content
.set_alignment(0, 0.5)
21 content
.set_selectable(True)
23 if hasattr(content
, 'set_padding'):
24 content
.set_padding(8, 4)
26 content
.set_border_width(8)
27 page
.pack_start(frame
, expand
, True, 0)
31 for x
in range(4, len(fp
), 4):
32 s
+= ' ' + fp
[x
:x
+ 4]
36 label
= gtk
.Label(text
)
37 label
.set_alignment(0, 0.5)
38 label
.set_selectable(True)
41 def make_hints_area(closed
, key_info_fetcher
):
44 for node
in parent
.childNodes
:
45 if node
.nodeType
== node
.TEXT_NODE
:
46 text
= text
+ node
.data
49 hints
= gtk
.VBox(False, 4)
53 infos
= set(key_info_fetcher
.info
) - shown
55 hints
.add(make_hint(info
.getAttribute("vote"), text(info
)))
58 if not(key_info_fetcher
.blocker
or shown
):
59 hints
.add(make_hint("bad", _('Warning: Nothing known about this key!')))
61 if key_info_fetcher
.blocker
:
62 status
= left(key_info_fetcher
.status
)
66 def update_when_ready():
67 while key_info_fetcher
.blocker
:
68 yield key_info_fetcher
.blocker
, closed
70 # The dialog box was closed. Stop updating.
81 def make_hint(vote
, hint_text
):
82 hint_icon
= gtk
.Image()
84 hint_icon
.set_from_stock(gtk
.STOCK_YES
, gtk
.ICON_SIZE_BUTTON
)
86 hint_icon
.set_from_stock(gtk
.STOCK_DIALOG_WARNING
, gtk
.ICON_SIZE_BUTTON
)
87 hint
= left(hint_text
)
88 hint
.set_line_wrap(True)
89 hint_hbox
= gtk
.HBox(False, 4)
90 hint_hbox
.pack_start(hint_icon
, False, True, 0)
91 hint_hbox
.pack_start(hint
, True, True, 0)
92 hint_icon
.set_alignment(0, 0)
96 class TrustBox(gtk
.Dialog
):
97 """Display a dialog box asking the user to confirm that one of the
98 keys is trusted for this domain.
103 def __init__(self
, pending
, valid_sigs
, parent
):
107 gtk
.Dialog
.__init
__(self
)
108 self
.set_has_separator(False)
109 self
.set_position(gtk
.WIN_POS_CENTER
)
110 self
.set_transient_for(parent
)
112 self
.closed
= tasks
.Blocker(_("confirming keys with user"))
114 domain
= trust
.domain_from_url(pending
.url
)
118 self
.closed
.trigger()
120 self
.connect('destroy', destroy
)
122 self
.set_title(_('Confirm trust'))
124 vbox
= gtk
.VBox(False, 4)
125 vbox
.set_border_width(4)
126 self
.vbox
.pack_start(vbox
, True, True, 0)
128 notebook
= gtk
.Notebook()
130 if len(valid_sigs
) == 1:
131 notebook
.set_show_tabs(False)
133 label
= left(_('Checking: %s') % pending
.url
)
134 label
.set_padding(4, 4)
135 vbox
.pack_start(label
, False, True, 0)
137 currently_trusted_keys
= trust
.trust_db
.get_keys_for_domain(domain
)
138 if currently_trusted_keys
:
139 keys
= [gpg
.load_key(fingerprint
) for fingerprint
in currently_trusted_keys
]
140 descriptions
= [_("%(key_name)s\n(fingerprint: %(key_fingerprint)s)") % {'key_name': key
.name
, 'key_fingerprint': pretty_fp(key
.fingerprint
)}
143 descriptions
= [_('None')]
144 frame(vbox
, _('Keys already approved for "%s"') % domain
, '\n'.join(descriptions
))
146 if len(valid_sigs
) == 1:
147 label
= left(_('This key signed the feed:'))
149 label
= left(_('These keys signed the feed:'))
151 label
.set_padding(4, 4)
152 vbox
.pack_start(label
, False, True, 0)
154 vbox
.pack_start(notebook
, True, True, 0)
156 self
.add_button(gtk
.STOCK_HELP
, gtk
.RESPONSE_HELP
)
157 self
.add_button(gtk
.STOCK_CANCEL
, gtk
.RESPONSE_CANCEL
)
158 self
.add_button(gtk
.STOCK_ADD
, gtk
.RESPONSE_OK
)
159 self
.set_default_response(gtk
.RESPONSE_OK
)
161 trust_checkbox
= {} # Sig -> CheckButton
164 for toggle
in trust_checkbox
.values():
165 if toggle
.get_active():
168 self
.set_response_sensitive(gtk
.RESPONSE_OK
, trust_any
)
170 for sig
in valid_sigs
:
171 if hasattr(sig
, 'get_details'):
173 details
= sig
.get_details()
175 if item
[0] in ('pub', 'uid') and \
181 page
= gtk
.VBox(False, 4)
182 page
.set_border_width(8)
184 frame(page
, _('Fingerprint'), pretty_fp(sig
.fingerprint
))
187 frame(page
, _('Claimed identity'), name
)
189 frame(page
, _('Unreliable hints database says'), make_hints_area(self
.closed
, valid_sigs
[sig
]))
191 already_trusted
= trust
.trust_db
.get_trust_domains(sig
.fingerprint
)
193 frame(page
, _('You already trust this key for these domains'),
194 '\n'.join(already_trusted
))
196 trust_checkbox
[sig
] = gtk
.CheckButton(_('_Trust this key'))
197 page
.pack_start(trust_checkbox
[sig
], False, True, 0)
198 trust_checkbox
[sig
].connect('toggled', lambda t
: ok_sensitive())
200 notebook
.append_page(page
, gtk
.Label(name
or 'Signature'))
205 def response(box
, resp
):
206 if resp
== gtk
.RESPONSE_HELP
:
209 if resp
== gtk
.RESPONSE_OK
:
210 self
.trust_keys([sig
for sig
in trust_checkbox
if trust_checkbox
[sig
].get_active()], domain
)
212 self
.connect('response', response
)
214 def trust_keys(self
, agreed_sigs
, domain
):
217 for sig
in agreed_sigs
:
218 trust
.trust_db
.trust_key(sig
.fingerprint
, domain
)
220 trust
.trust_db
.notify()
221 except Exception, ex
:
222 gtkutils
.show_message_box(self
, str(ex
), gtk
.MESSAGE_ERROR
)
223 if not isinstance(ex
, SafeException
):
226 trust_help
= help_box
.HelpBox(_("Trust Help"),
227 (_('Overview'), '\n' +
228 _("""When you run a program, it typically has access to all your files and can generally do \
229 anything that you're allowed to do (delete files, send emails, etc). So it's important \
230 to make sure that you don't run anything malicious.""")),
232 (_('Digital signatures'), '\n' +
233 _("""Each software author creates a 'key-pair'; a 'public key' and a 'private key'. Without going \
234 into the maths, only something encrypted with the private key will decrypt with the public key.
236 So, when a programmer releases some software, they encrypt it with their private key (which no-one \
237 else has). When you download it, the injector checks that it decrypts using their public key, thus \
238 proving that it came from them and hasn't been tampered with.""")),
241 _("""After the injector has checked that the software hasn't been modified since it was signed with \
242 the private key, you still have the following problems:
244 1. Does the public key you have really belong to the author?
245 2. Even if the software really did come from that person, do you trust them?""")),
247 (_('Key fingerprints'), '\n' +
248 _("""To confirm (1), you should compare the public key you have with the genuine one. To make this \
249 easier, the injector displays a 'fingerprint' for the key. Look in mailing list postings or some \
250 other source to check that the fingerprint is right (a different key will have a different \
253 You're trying to protect against the situation where an attacker breaks into a web site \
254 and puts up malicious software, signed with the attacker's private key, and puts up the \
255 attacker's public key too. If you've downloaded this software before, you \
256 should be suspicious that you're being asked to confirm another key!""")),
258 (_('Reputation'), '\n' +
259 _("""In general, most problems seem to come from malicous and otherwise-unknown people \
260 replacing software with modified versions, or creating new programs intended only to \
261 cause damage. So, check your programs are signed by a key with a good reputation!""")))