121
116
return (count, result, all_verifiable)
123
118
def valid_commits_message(self, count):
124
return i18n.gettext(u"{0} commits with valid signatures").format(
125
count[SIGNATURE_VALID])
119
return gettext(u"{0} commits with valid signatures").format(
120
count[SIGNATURE_VALID])
127
122
def unknown_key_message(self, count):
128
return i18n.ngettext(u"{0} commit with unknown key",
123
return ngettext(u"{0} commit with unknown key",
129
124
u"{0} commits with unknown keys",
130
125
count[SIGNATURE_KEY_MISSING]).format(
131
126
count[SIGNATURE_KEY_MISSING])
133
128
def commit_not_valid_message(self, count):
134
return i18n.ngettext(u"{0} commit not valid",
129
return ngettext(u"{0} commit not valid",
135
130
u"{0} commits not valid",
136
131
count[SIGNATURE_NOT_VALID]).format(
137
132
count[SIGNATURE_NOT_VALID])
139
134
def commit_not_signed_message(self, count):
140
return i18n.ngettext(u"{0} commit not signed",
135
return ngettext(u"{0} commit not signed",
141
136
u"{0} commits not signed",
142
137
count[SIGNATURE_NOT_SIGNED]).format(
143
138
count[SIGNATURE_NOT_SIGNED])
140
def expired_commit_message(self, count):
141
return ngettext(u"{0} commit with key now expired",
142
u"{0} commits with key now expired",
143
count[SIGNATURE_EXPIRED]).format(
144
count[SIGNATURE_EXPIRED])
146
147
def _set_gpg_tty():
147
148
tty = os.environ.get('TTY')
177
186
def _command_line(self):
179
return [self._config.gpg_signing_command(), '--clearsign', '-u',
180
self._config.gpg_signing_key()]
182
def __init__(self, config):
183
self._config = config
186
self.context = gpgme.Context()
187
except ImportError, error:
188
pass # can't use verify()
187
key = self._config_stack.get('gpg_signing_key')
188
if key is None or key == 'default':
189
# 'default' or not setting gpg_signing_key at all means we should
190
# use the user email address
191
key = config.extract_email_address(self._config_stack.get('email'))
192
return [self._config_stack.get('gpg_signing_command'), '--clearsign',
193
'-u', key, '--no-tty']
190
195
def sign(self, content):
191
196
if isinstance(content, unicode):
239
244
signature = StringIO(content)
240
245
plain_output = StringIO()
243
247
result = self.context.verify(signature, None, plain_output)
244
248
except gpgme.GpgmeError,error:
245
249
raise errors.SignatureVerificationFailed(error[2])
251
# No result if input is invalid.
252
# test_verify_invalid()
247
253
if len(result) == 0:
248
254
return SIGNATURE_NOT_VALID, None
255
# User has specified a list of acceptable keys, check our result is in
256
# it. test_verify_unacceptable_key()
249
257
fingerprint = result[0].fpr
250
258
if self.acceptable_keys is not None:
251
259
if not fingerprint in self.acceptable_keys:
252
260
return SIGNATURE_KEY_MISSING, fingerprint[-8:]
261
# Check the signature actually matches the testament.
262
# test_verify_bad_testament()
253
263
if testament != plain_output.getvalue():
254
264
return SIGNATURE_NOT_VALID, None
265
# Yay gpgme set the valid bit.
266
# Can't write a test for this one as you can't set a key to be
267
# trusted using gpgme.
255
268
if result[0].summary & gpgme.SIGSUM_VALID:
256
269
key = self.context.get_key(fingerprint)
257
270
name = key.uids[0].name
258
271
email = key.uids[0].email
259
272
return SIGNATURE_VALID, name + " <" + email + ">"
273
# Sigsum_red indicates a problem, unfortunatly I have not been able
274
# to write any tests which actually set this.
260
275
if result[0].summary & gpgme.SIGSUM_RED:
261
276
return SIGNATURE_NOT_VALID, None
277
# GPG does not know this key.
278
# test_verify_unknown_key()
262
279
if result[0].summary & gpgme.SIGSUM_KEY_MISSING:
263
280
return SIGNATURE_KEY_MISSING, fingerprint[-8:]
264
#summary isn't set if sig is valid but key is untrusted
281
# Summary isn't set if sig is valid but key is untrusted but if user
282
# has explicity set the key as acceptable we can validate it.
265
283
if result[0].summary == 0 and self.acceptable_keys is not None:
266
284
if fingerprint in self.acceptable_keys:
285
# test_verify_untrusted_but_accepted()
267
286
return SIGNATURE_VALID, None
269
return SIGNATURE_KEY_MISSING, None
287
# test_verify_valid_but_untrusted()
288
if result[0].summary == 0 and self.acceptable_keys is None:
289
return SIGNATURE_NOT_VALID, None
290
if result[0].summary & gpgme.SIGSUM_KEY_EXPIRED:
291
expires = self.context.get_key(result[0].fpr).subkeys[0].expires
292
if expires > result[0].timestamp:
293
# The expired key was not expired at time of signing.
294
# test_verify_expired_but_valid()
295
return SIGNATURE_EXPIRED, fingerprint[-8:]
297
# I can't work out how to create a test where the signature
298
# was expired at the time of signing.
299
return SIGNATURE_NOT_VALID, None
300
# A signature from a revoked key gets this.
301
# test_verify_revoked_signature()
302
if result[0].summary & gpgme.SIGSUM_SYS_ERROR:
303
return SIGNATURE_NOT_VALID, None
304
# Other error types such as revoked keys should (I think) be caught by
305
# SIGSUM_RED so anything else means something is buggy.
270
306
raise errors.SignatureVerificationFailed("Unknown GnuPG key "\
271
307
"verification result")
273
309
def set_acceptable_keys(self, command_line_input):
274
"""sets the acceptable keys for verifying with this GPGStrategy
310
"""Set the acceptable keys for verifying with this GPGStrategy.
276
312
:param command_line_input: comma separated list of patterns from
280
316
key_patterns = None
281
acceptable_keys_config = self._config.acceptable_keys()
317
acceptable_keys_config = self._config_stack.get('acceptable_keys')
283
319
if isinstance(acceptable_keys_config, unicode):
284
320
acceptable_keys_config = str(acceptable_keys_config)
285
321
except UnicodeEncodeError:
286
#gpg Context.keylist(pattern) does not like unicode
287
raise errors.BzrCommandError('Only ASCII permitted in option names')
322
# gpg Context.keylist(pattern) does not like unicode
323
raise errors.BzrCommandError(
324
gettext('Only ASCII permitted in option names'))
289
326
if acceptable_keys_config is not None:
290
327
key_patterns = acceptable_keys_config
291
if command_line_input is not None: #command line overrides config
328
if command_line_input is not None: # command line overrides config
292
329
key_patterns = command_line_input
293
330
if key_patterns is not None:
294
331
patterns = key_patterns.split(",")
392
430
signers[fingerprint] += 1
394
432
for fingerprint, number in signers.items():
395
result.append( i18n.ngettext(u"Unknown key {0} signed {1} commit",
433
result.append( ngettext(u"Unknown key {0} signed {1} commit",
396
434
u"Unknown key {0} signed {1} commits",
397
435
number).format(fingerprint, number) )
438
def verbose_expired_key_message(self, result, repo):
439
"""takes a verify result and returns list of expired key info"""
441
fingerprint_to_authors = {}
442
for rev_id, validity, fingerprint in result:
443
if validity == SIGNATURE_EXPIRED:
444
revision = repo.get_revision(rev_id)
445
authors = ', '.join(revision.get_apparent_authors())
446
signers.setdefault(fingerprint, 0)
447
signers[fingerprint] += 1
448
fingerprint_to_authors[fingerprint] = authors
450
for fingerprint, number in signers.items():
452
ngettext(u"{0} commit by author {1} with key {2} now expired",
453
u"{0} commits by author {1} with key {2} now expired",
455
number, fingerprint_to_authors[fingerprint], fingerprint) )
400
458
def valid_commits_message(self, count):
401
459
"""returns message for number of commits"""
402
return i18n.gettext(u"{0} commits with valid signatures").format(
460
return gettext(u"{0} commits with valid signatures").format(
403
461
count[SIGNATURE_VALID])
405
463
def unknown_key_message(self, count):
406
464
"""returns message for number of commits"""
407
return i18n.ngettext(u"{0} commit with unknown key",
408
u"{0} commits with unknown keys",
409
count[SIGNATURE_KEY_MISSING]).format(
465
return ngettext(u"{0} commit with unknown key",
466
u"{0} commits with unknown keys",
467
count[SIGNATURE_KEY_MISSING]).format(
410
468
count[SIGNATURE_KEY_MISSING])
412
470
def commit_not_valid_message(self, count):
413
471
"""returns message for number of commits"""
414
return i18n.ngettext(u"{0} commit not valid",
415
u"{0} commits not valid",
416
count[SIGNATURE_NOT_VALID]).format(
472
return ngettext(u"{0} commit not valid",
473
u"{0} commits not valid",
474
count[SIGNATURE_NOT_VALID]).format(
417
475
count[SIGNATURE_NOT_VALID])
419
477
def commit_not_signed_message(self, count):
420
478
"""returns message for number of commits"""
421
return i18n.ngettext(u"{0} commit not signed",
422
u"{0} commits not signed",
423
count[SIGNATURE_NOT_SIGNED]).format(
479
return ngettext(u"{0} commit not signed",
480
u"{0} commits not signed",
481
count[SIGNATURE_NOT_SIGNED]).format(
424
482
count[SIGNATURE_NOT_SIGNED])
484
def expired_commit_message(self, count):
485
"""returns message for number of commits"""
486
return ngettext(u"{0} commit with key now expired",
487
u"{0} commits with key now expired",
488
count[SIGNATURE_EXPIRED]).format(
489
count[SIGNATURE_EXPIRED])