226
175
:return: boolean if this strategy can verify signatures
229
import gpg # noqa: F401
180
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.')
183
def _command_line(self):
184
key = self._config_stack.get('gpg_signing_key')
185
if key is None or key == 'default':
186
# 'default' or not setting gpg_signing_key at all means we should
187
# use the user email address
188
key = config.extract_email_address(self._config_stack.get('email'))
189
return [self._config_stack.get('gpg_signing_command'), '--clearsign',
241
if isinstance(content, str):
192
def sign(self, content):
193
if isinstance(content, unicode):
242
194
raise errors.BzrBadParameterUnicode('content')
195
ui.ui_factory.clear_term()
244
plain_text = gpg.Data(content)
197
preexec_fn = _set_gpg_tty
198
if sys.platform == 'win32':
199
# Win32 doesn't support preexec_fn, but wouldn't support TTY anyway.
246
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,
252
except gpg.errors.GPGMEError as error:
253
raise SigningFailed(str(error))
257
def verify(self, signed_data, signature=None):
202
process = subprocess.Popen(self._command_line(),
203
stdin=subprocess.PIPE,
204
stdout=subprocess.PIPE,
205
preexec_fn=preexec_fn)
207
result = process.communicate(content)[0]
208
if process.returncode is None:
210
if process.returncode != 0:
211
raise errors.SigningFailed(self._command_line())
214
if e.errno == errno.EPIPE:
215
raise errors.SigningFailed(self._command_line())
219
# bad subprocess parameters, should never happen.
222
if e.errno == errno.ENOENT:
223
# gpg is not installed
224
raise errors.SigningFailed(self._command_line())
228
def verify(self, content, testament):
258
229
"""Check content has a valid signature.
260
:param signed_data; Signed data
261
:param signature: optional signature (if detached)
231
:param content: the commit signature
232
:param testament: the valid testament string for the commit
263
:return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid, plain text
234
:return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid
267
238
except ImportError as error:
268
raise GpgNotInstalled(
269
'Set check_signatures=ignore to disable verifying signatures.')
239
raise errors.GpgmeNotInstalled(error)
271
signed_data = gpg.Data(signed_data)
273
signature = gpg.Data(signature)
241
signature = BytesIO(content)
242
plain_output = BytesIO()
275
plain_output, result = self.context.verify(signed_data, signature)
276
except gpg.errors.BadSignatures as error:
277
fingerprint = error.result.signatures[0].fpr
278
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
281
if expires > error.result.signatures[0].timestamp:
282
# The expired key was not expired at time of signing.
283
# test_verify_expired_but_valid()
284
return SIGNATURE_EXPIRED, fingerprint[-8:], None
286
# I can't work out how to create a test where the signature
287
# was expired at the time of signing.
288
return SIGNATURE_NOT_VALID, None, None
290
# GPG does not know this key.
291
# 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
296
return SIGNATURE_NOT_VALID, None, None
297
except gpg.errors.GPGMEError as error:
298
raise SignatureVerificationFailed(error)
244
result = self.context.verify(signature, None, plain_output)
245
except gpgme.GpgmeError as error:
246
raise errors.SignatureVerificationFailed(error[2])
300
248
# No result if input is invalid.
301
249
# test_verify_invalid()
302
if len(result.signatures) == 0:
303
return SIGNATURE_NOT_VALID, None, plain_output
251
return SIGNATURE_NOT_VALID, None
305
252
# User has specified a list of acceptable keys, check our result is in
306
253
# it. test_verify_unacceptable_key()
307
fingerprint = result.signatures[0].fpr
254
fingerprint = result[0].fpr
308
255
if self.acceptable_keys is not None:
309
if fingerprint not in self.acceptable_keys:
310
return SIGNATURE_KEY_MISSING, fingerprint[-8:], plain_output
311
# Yay gpg set the valid bit.
256
if not fingerprint in self.acceptable_keys:
257
return SIGNATURE_KEY_MISSING, fingerprint[-8:]
258
# Check the signature actually matches the testament.
259
# test_verify_bad_testament()
260
if testament != plain_output.getvalue():
261
return SIGNATURE_NOT_VALID, None
262
# Yay gpgme set the valid bit.
312
263
# Can't write a test for this one as you can't set a key to be
314
if result.signatures[0].summary & gpg.constants.SIGSUM_VALID:
264
# trusted using gpgme.
265
if result[0].summary & gpgme.SIGSUM_VALID:
315
266
key = self.context.get_key(fingerprint)
316
267
name = key.uids[0].name
317
if isinstance(name, bytes):
318
name = name.decode('utf-8')
319
268
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)
269
return SIGNATURE_VALID, name + " <" + email + ">"
323
270
# Sigsum_red indicates a problem, unfortunatly I have not been able
324
271
# to write any tests which actually set this.
325
if result.signatures[0].summary & gpg.constants.SIGSUM_RED:
326
return SIGNATURE_NOT_VALID, None, plain_output
272
if result[0].summary & gpgme.SIGSUM_RED:
273
return SIGNATURE_NOT_VALID, None
274
# GPG does not know this key.
275
# test_verify_unknown_key()
276
if result[0].summary & gpgme.SIGSUM_KEY_MISSING:
277
return SIGNATURE_KEY_MISSING, fingerprint[-8:]
327
278
# Summary isn't set if sig is valid but key is untrusted but if user
328
279
# 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):
280
if result[0].summary == 0 and self.acceptable_keys is not None:
331
281
if fingerprint in self.acceptable_keys:
332
282
# test_verify_untrusted_but_accepted()
333
return SIGNATURE_VALID, None, plain_output
283
return SIGNATURE_VALID, None
334
284
# test_verify_valid_but_untrusted()
335
if result.signatures[0].summary == 0 and self.acceptable_keys is None:
336
return SIGNATURE_NOT_VALID, None, plain_output
285
if result[0].summary == 0 and self.acceptable_keys is None:
286
return SIGNATURE_NOT_VALID, None
287
if result[0].summary & gpgme.SIGSUM_KEY_EXPIRED:
288
expires = self.context.get_key(result[0].fpr).subkeys[0].expires
289
if expires > result[0].timestamp:
290
# The expired key was not expired at time of signing.
291
# test_verify_expired_but_valid()
292
return SIGNATURE_EXPIRED, fingerprint[-8:]
294
# I can't work out how to create a test where the signature
295
# was expired at the time of signing.
296
return SIGNATURE_NOT_VALID, None
297
# A signature from a revoked key gets this.
298
# test_verify_revoked_signature()
299
if ((result[0].summary & gpgme.SIGSUM_SYS_ERROR
300
or result[0].status.strerror == 'Certificate revoked')):
301
return SIGNATURE_NOT_VALID, None
337
302
# Other error types such as revoked keys should (I think) be caught by
338
303
# SIGSUM_RED so anything else means something is buggy.
339
raise SignatureVerificationFailed(
304
raise errors.SignatureVerificationFailed(
340
305
"Unknown GnuPG key verification result")
342
307
def set_acceptable_keys(self, command_line_input):