/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/gpg.py

  • Committer: Jelmer Vernooij
  • Date: 2018-06-14 17:59:16 UTC
  • mto: This revision was merged to the branch mainline in revision 7065.
  • Revision ID: jelmer@jelmer.uk-20180614175916-a2e2xh5k533guq1x
Move breezy.plugins.git to breezy.git.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
"""GPG signing and checking logic."""
19
19
 
 
20
from __future__ import absolute_import
 
21
 
20
22
import os
 
23
import sys
21
24
 
22
25
from breezy.lazy_import import lazy_import
23
26
lazy_import(globals(), """
 
27
import errno
 
28
import subprocess
 
29
 
24
30
from breezy import (
25
31
    config,
26
32
    trace,
35
41
from . import (
36
42
    errors,
37
43
    )
 
44
from .sixish import (
 
45
    BytesIO,
 
46
    )
38
47
 
39
 
# verification results
 
48
#verification results
40
49
SIGNATURE_VALID = 0
41
50
SIGNATURE_KEY_MISSING = 1
42
51
SIGNATURE_NOT_VALID = 2
50
59
 
51
60
class GpgNotInstalled(errors.DependencyNotPresent):
52
61
 
53
 
    _fmt = ('python-gpg is not installed, it is needed to create or '
54
 
            'verify signatures. %(error)s')
 
62
    _fmt = 'python-gpg is not installed, it is needed to verify signatures'
55
63
 
56
64
    def __init__(self, error):
57
65
        errors.DependencyNotPresent.__init__(self, 'gpg', error)
74
82
 
75
83
 
76
84
def bulk_verify_signatures(repository, revids, strategy,
77
 
                           process_events_callback=None):
 
85
        process_events_callback=None):
78
86
    """Do verifications on a set of revisions
79
87
 
80
88
    :param repository: repository object
143
151
        """Real strategies take a configuration."""
144
152
 
145
153
    def sign(self, content, mode):
146
 
        return (b"-----BEGIN PSEUDO-SIGNED CONTENT-----\n" + content
147
 
                + b"-----END PSEUDO-SIGNED CONTENT-----\n")
 
154
        return ("-----BEGIN PSEUDO-SIGNED CONTENT-----\n" + content +
 
155
                "-----END PSEUDO-SIGNED CONTENT-----\n")
148
156
 
149
157
    def verify(self, signed_data, signature=None):
150
 
        plain_text = signed_data.replace(
151
 
            b"-----BEGIN PSEUDO-SIGNED CONTENT-----\n", b"")
152
 
        plain_text = plain_text.replace(
153
 
            b"-----END PSEUDO-SIGNED CONTENT-----\n", b"")
 
158
        plain_text = signed_data.replace("-----BEGIN PSEUDO-SIGNED CONTENT-----\n", "")
 
159
        plain_text = plain_text.replace("-----END PSEUDO-SIGNED CONTENT-----\n", "")
154
160
        return SIGNATURE_VALID, None, plain_text
155
161
 
156
162
    def set_acceptable_keys(self, command_line_input):
172
178
    else:
173
179
        # This is not quite worthy of a warning, because some people
174
180
        # don't need GPG_TTY to be set. But it is worthy of a big mark
175
 
        # in brz.log, so that people can debug it if it happens to them
 
181
        # in ~/.brz.log, so that people can debug it if it happens to them
176
182
        trace.mutter('** Env var TTY empty, cannot set GPG_TTY.'
177
183
                     '  Is TTY exported?')
178
184
 
189
195
            self.context = gpg.Context()
190
196
            self.context.armor = True
191
197
            self.context.signers = self._get_signing_keys()
192
 
        except ImportError:
193
 
            pass  # can't use verify()
 
198
        except ImportError as error:
 
199
            pass # can't use verify()
194
200
 
195
201
    def _get_signing_keys(self):
196
202
        import gpg
197
203
        keyname = self._config_stack.get('gpg_signing_key')
198
 
        if keyname == 'default':
199
 
            # Leave things to gpg
200
 
            return []
201
 
 
202
204
        if keyname:
203
205
            try:
204
206
                return [self.context.get_key(keyname)]
205
207
            except gpg.errors.KeyNotFound:
206
208
                pass
207
209
 
208
 
        if keyname is None:
209
 
            # not setting gpg_signing_key at all means we should
 
210
        if keyname is None or keyname == 'default':
 
211
            # 'default' or not setting gpg_signing_key at all means we should
210
212
            # use the user email address
211
 
            keyname = config.extract_email_address(
212
 
                self._config_stack.get('email'))
213
 
        if keyname == 'default':
214
 
            return []
 
213
            keyname = config.extract_email_address(self._config_stack.get('email'))
215
214
        possible_keys = self.context.keylist(keyname, secret=True)
216
215
        try:
217
 
            return [next(possible_keys)]
 
216
            return [possible_keys.next()]
218
217
        except StopIteration:
219
218
            return []
220
219
 
226
225
        :return: boolean if this strategy can verify signatures
227
226
        """
228
227
        try:
229
 
            import gpg  # noqa: F401
 
228
            import gpg
230
229
            return True
231
 
        except ImportError:
 
230
        except ImportError as error:
232
231
            return False
233
232
 
234
233
    def sign(self, content, mode):
235
 
        try:
236
 
            import gpg
237
 
        except ImportError as error:
238
 
            raise GpgNotInstalled(
239
 
                'Set create_signatures=no to disable creating signatures.')
240
 
 
241
 
        if isinstance(content, str):
 
234
        import gpg
 
235
        if isinstance(content, unicode):
242
236
            raise errors.BzrBadParameterUnicode('content')
243
237
 
244
238
        plain_text = gpg.Data(content)
265
259
        try:
266
260
            import gpg
267
261
        except ImportError as error:
268
 
            raise GpgNotInstalled(
269
 
                'Set check_signatures=ignore to disable verifying signatures.')
 
262
            raise errors.GpgNotInstalled(error)
270
263
 
271
264
        signed_data = gpg.Data(signed_data)
272
265
        if signature:
276
269
        except gpg.errors.BadSignatures as error:
277
270
            fingerprint = error.result.signatures[0].fpr
278
271
            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
 
272
                expires = self.context.get_key(error.result.signatures[0].fpr).subkeys[0].expires
281
273
                if expires > error.result.signatures[0].timestamp:
282
274
                    # The expired key was not expired at time of signing.
283
275
                    # test_verify_expired_but_valid()
289
281
 
290
282
            # GPG does not know this key.
291
283
            # test_verify_unknown_key()
292
 
            if (error.result.signatures[0].summary &
293
 
                    gpg.constants.SIGSUM_KEY_MISSING):
 
284
            if error.result.signatures[0].summary & gpg.constants.SIGSUM_KEY_MISSING:
294
285
                return SIGNATURE_KEY_MISSING, fingerprint[-8:], None
295
286
 
296
287
            return SIGNATURE_NOT_VALID, None, None
306
297
        # it.  test_verify_unacceptable_key()
307
298
        fingerprint = result.signatures[0].fpr
308
299
        if self.acceptable_keys is not None:
309
 
            if fingerprint not in self.acceptable_keys:
 
300
            if not fingerprint in self.acceptable_keys:
310
301
                return SIGNATURE_KEY_MISSING, fingerprint[-8:], plain_output
311
302
        # Yay gpg set the valid bit.
312
303
        # Can't write a test for this one as you can't set a key to be
314
305
        if result.signatures[0].summary & gpg.constants.SIGSUM_VALID:
315
306
            key = self.context.get_key(fingerprint)
316
307
            name = key.uids[0].name
317
 
            if isinstance(name, bytes):
318
 
                name = name.decode('utf-8')
319
308
            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)
 
309
            return SIGNATURE_VALID, name.decode('utf-8') + u" <" + email.decode('utf-8') + u">", plain_output
323
310
        # Sigsum_red indicates a problem, unfortunatly I have not been able
324
311
        # to write any tests which actually set this.
325
312
        if result.signatures[0].summary & gpg.constants.SIGSUM_RED:
326
313
            return SIGNATURE_NOT_VALID, None, plain_output
327
314
        # Summary isn't set if sig is valid but key is untrusted but if user
328
315
        # 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):
 
316
        if result.signatures[0].summary == 0 and self.acceptable_keys is not None:
331
317
            if fingerprint in self.acceptable_keys:
332
318
                # test_verify_untrusted_but_accepted()
333
319
                return SIGNATURE_VALID, None, plain_output
350
336
        acceptable_keys_config = self._config_stack.get('acceptable_keys')
351
337
        if acceptable_keys_config is not None:
352
338
            patterns = acceptable_keys_config
353
 
        if command_line_input is not None:  # command line overrides config
 
339
        if command_line_input is not None: # command line overrides config
354
340
            patterns = command_line_input.split(',')
355
341
 
356
342
        if patterns:
364
350
                    trace.mutter("Added acceptable key: " + key.subkeys[0].fpr)
365
351
                if not found_key:
366
352
                    trace.note(gettext(
367
 
                        "No GnuPG key results for pattern: {0}"
368
 
                        ).format(pattern))
 
353
                            "No GnuPG key results for pattern: {0}"
 
354
                                ).format(pattern))
369
355
 
370
356
 
371
357
def valid_commits_message(count):
372
358
    """returns message for number of commits"""
373
359
    return gettext(u"{0} commits with valid signatures").format(
374
 
        count[SIGNATURE_VALID])
 
360
                                    count[SIGNATURE_VALID])
375
361
 
376
362
 
377
363
def unknown_key_message(count):
379
365
    return ngettext(u"{0} commit with unknown key",
380
366
                    u"{0} commits with unknown keys",
381
367
                    count[SIGNATURE_KEY_MISSING]).format(
382
 
        count[SIGNATURE_KEY_MISSING])
 
368
                                    count[SIGNATURE_KEY_MISSING])
383
369
 
384
370
 
385
371
def commit_not_valid_message(count):
387
373
    return ngettext(u"{0} commit not valid",
388
374
                    u"{0} commits not valid",
389
375
                    count[SIGNATURE_NOT_VALID]).format(
390
 
        count[SIGNATURE_NOT_VALID])
 
376
                                        count[SIGNATURE_NOT_VALID])
391
377
 
392
378
 
393
379
def commit_not_signed_message(count):
395
381
    return ngettext(u"{0} commit not signed",
396
382
                    u"{0} commits not signed",
397
383
                    count[SIGNATURE_NOT_SIGNED]).format(
398
 
        count[SIGNATURE_NOT_SIGNED])
 
384
                                    count[SIGNATURE_NOT_SIGNED])
399
385
 
400
386
 
401
387
def expired_commit_message(count):
403
389
    return ngettext(u"{0} commit with key now expired",
404
390
                    u"{0} commits with key now expired",
405
391
                    count[SIGNATURE_EXPIRED]).format(
406
 
        count[SIGNATURE_EXPIRED])
 
392
                                count[SIGNATURE_EXPIRED])
407
393
 
408
394
 
409
395
def verbose_expired_key_message(result, repo):
436
422
            signers[uid] += 1
437
423
    result = []
438
424
    for uid, number in signers.items():
439
 
        result.append(ngettext(u"{0} signed {1} commit",
440
 
                               u"{0} signed {1} commits",
441
 
                               number).format(uid, number))
 
425
         result.append(ngettext(u"{0} signed {1} commit",
 
426
                                u"{0} signed {1} commits",
 
427
                                number).format(uid, number))
442
428
    return result
443
429
 
444
430