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