222
226
:return: boolean if this strategy can verify signatures
229
import gpg # noqa: F401
227
except ImportError as error:
230
def sign(self, content):
232
if isinstance(content, unicode):
234
def sign(self, content, mode):
237
except ImportError as error:
238
raise GpgNotInstalled(
239
'Set create_signatures=no to disable creating signatures.')
241
if isinstance(content, str):
233
242
raise errors.BzrBadParameterUnicode('content')
235
244
plain_text = gpg.Data(content)
237
246
output, result = self.context.sign(
238
plain_text, mode=gpg.constants.sig.mode.CLEAR)
248
MODE_DETACH: gpg.constants.sig.mode.DETACH,
249
MODE_CLEAR: gpg.constants.sig.mode.CLEAR,
250
MODE_NORMAL: gpg.constants.sig.mode.NORMAL,
239
252
except gpg.errors.GPGMEError as error:
240
253
raise SigningFailed(str(error))
244
def verify(self, content, testament):
257
def verify(self, signed_data, signature=None):
245
258
"""Check content has a valid signature.
247
:param content: the commit signature
248
:param testament: the valid testament string for the commit
260
:param signed_data; Signed data
261
:param signature: optional signature (if detached)
250
:return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid
263
:return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid, plain text
254
267
except ImportError as error:
255
raise errors.GpgNotInstalled(error)
268
raise GpgNotInstalled(
269
'Set check_signatures=ignore to disable verifying signatures.')
257
signature = gpg.Data(content)
271
signed_data = gpg.Data(signed_data)
273
signature = gpg.Data(signature)
260
plain_output, result = self.context.verify(signature)
275
plain_output, result = self.context.verify(signed_data, signature)
261
276
except gpg.errors.BadSignatures as error:
262
277
fingerprint = error.result.signatures[0].fpr
263
278
if error.result.signatures[0].summary & gpg.constants.SIGSUM_KEY_EXPIRED:
264
expires = self.context.get_key(error.result.signatures[0].fpr).subkeys[0].expires
279
expires = self.context.get_key(
280
error.result.signatures[0].fpr).subkeys[0].expires
265
281
if expires > error.result.signatures[0].timestamp:
266
282
# The expired key was not expired at time of signing.
267
283
# test_verify_expired_but_valid()
268
return SIGNATURE_EXPIRED, fingerprint[-8:]
284
return SIGNATURE_EXPIRED, fingerprint[-8:], None
270
286
# I can't work out how to create a test where the signature
271
287
# was expired at the time of signing.
272
return SIGNATURE_NOT_VALID, None
288
return SIGNATURE_NOT_VALID, None, None
274
290
# GPG does not know this key.
275
291
# test_verify_unknown_key()
276
if error.result.signatures[0].summary & gpg.constants.SIGSUM_KEY_MISSING:
277
return SIGNATURE_KEY_MISSING, fingerprint[-8:]
292
if (error.result.signatures[0].summary &
293
gpg.constants.SIGSUM_KEY_MISSING):
294
return SIGNATURE_KEY_MISSING, fingerprint[-8:], None
279
return SIGNATURE_NOT_VALID, None
296
return SIGNATURE_NOT_VALID, None, None
280
297
except gpg.errors.GPGMEError as error:
281
raise SignatureVerificationFailed(error[2])
298
raise SignatureVerificationFailed(error)
283
300
# No result if input is invalid.
284
301
# test_verify_invalid()
285
302
if len(result.signatures) == 0:
286
return SIGNATURE_NOT_VALID, None
303
return SIGNATURE_NOT_VALID, None, plain_output
287
305
# User has specified a list of acceptable keys, check our result is in
288
306
# it. test_verify_unacceptable_key()
289
307
fingerprint = result.signatures[0].fpr
290
308
if self.acceptable_keys is not None:
291
if not fingerprint in self.acceptable_keys:
292
return SIGNATURE_KEY_MISSING, fingerprint[-8:]
293
# Check the signature actually matches the testament.
294
# test_verify_bad_testament()
295
if testament != plain_output:
296
return SIGNATURE_NOT_VALID, None
309
if fingerprint not in self.acceptable_keys:
310
return SIGNATURE_KEY_MISSING, fingerprint[-8:], plain_output
297
311
# Yay gpg set the valid bit.
298
312
# Can't write a test for this one as you can't set a key to be
299
313
# trusted using gpg.
300
314
if result.signatures[0].summary & gpg.constants.SIGSUM_VALID:
301
315
key = self.context.get_key(fingerprint)
302
316
name = key.uids[0].name
317
if isinstance(name, bytes):
318
name = name.decode('utf-8')
303
319
email = key.uids[0].email
304
return SIGNATURE_VALID, name + " <" + email + ">"
320
if isinstance(email, bytes):
321
email = email.decode('utf-8')
322
return (SIGNATURE_VALID, name + u" <" + email + u">", plain_output)
305
323
# Sigsum_red indicates a problem, unfortunatly I have not been able
306
324
# to write any tests which actually set this.
307
325
if result.signatures[0].summary & gpg.constants.SIGSUM_RED:
308
return SIGNATURE_NOT_VALID, None
326
return SIGNATURE_NOT_VALID, None, plain_output
309
327
# Summary isn't set if sig is valid but key is untrusted but if user
310
328
# has explicity set the key as acceptable we can validate it.
311
if result.signatures[0].summary == 0 and self.acceptable_keys is not None:
329
if (result.signatures[0].summary == 0 and
330
self.acceptable_keys is not None):
312
331
if fingerprint in self.acceptable_keys:
313
332
# test_verify_untrusted_but_accepted()
314
return SIGNATURE_VALID, None
333
return SIGNATURE_VALID, None, plain_output
315
334
# test_verify_valid_but_untrusted()
316
335
if result.signatures[0].summary == 0 and self.acceptable_keys is None:
317
return SIGNATURE_NOT_VALID, None
336
return SIGNATURE_NOT_VALID, None, plain_output
318
337
# Other error types such as revoked keys should (I think) be caught by
319
338
# SIGSUM_RED so anything else means something is buggy.
320
339
raise SignatureVerificationFailed(