Status: | Superseded |
---|---|
Proposed branch: | lp:~jelmer/brz/gpg |
Merge into: | lp:brz |
Diff against target: |
781 lines (+137/-241) 11 files modified
breezy/config.py (+0/-17) breezy/errors.py (+0/-24) breezy/gpg.py (+97/-81) breezy/help_topics/en/configuration.txt (+0/-10) breezy/tests/blackbox/test_log.py (+5/-2) breezy/tests/features.py (+3/-1) breezy/tests/test_commit.py (+1/-5) breezy/tests/test_config.py (+0/-2) breezy/tests/test_gpg.py (+24/-97) breezy/tests/test_merge_directive.py (+0/-2) doc/en/release-notes/brz-3.0.txt (+7/-0) |
To merge this branch: | bzr merge lp:~jelmer/brz/gpg |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Packman | Pending | ||
Review via email: mp+326812@code.launchpad.net |
This proposal has been superseded by a proposal from 2017-07-04.
Commit message
Switch to using python-gpg (part of gpgme) rather than deprecated python-gpgme.
Description of the change
Switch to using python-gpg (part of gpgme) rather than deprecated python-gpgme.
Also, switch to using gpgme for signing, while we're at it.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'breezy/config.py' | |||
2 | --- breezy/config.py 2017-06-15 01:07:35 +0000 | |||
3 | +++ breezy/config.py 2017-07-04 21:10:26 +0000 | |||
4 | @@ -27,7 +27,6 @@ | |||
5 | 27 | email=Your Name <your@email.address> | 27 | email=Your Name <your@email.address> |
6 | 28 | check_signatures=require|ignore|check-available(default) | 28 | check_signatures=require|ignore|check-available(default) |
7 | 29 | create_signatures=always|never|when-required(default) | 29 | create_signatures=always|never|when-required(default) |
8 | 30 | gpg_signing_command=name-of-program | ||
9 | 31 | log_format=name-of-format | 30 | log_format=name-of-format |
10 | 32 | validate_signatures_in_log=true|false(default) | 31 | validate_signatures_in_log=true|false(default) |
11 | 33 | acceptable_keys=pattern1,pattern2 | 32 | acceptable_keys=pattern1,pattern2 |
12 | @@ -798,10 +797,6 @@ | |||
13 | 798 | else: | 797 | else: |
14 | 799 | return None | 798 | return None |
15 | 800 | 799 | ||
16 | 801 | def _gpg_signing_command(self): | ||
17 | 802 | """See Config.gpg_signing_command.""" | ||
18 | 803 | return self._get_user_option('gpg_signing_command') | ||
19 | 804 | |||
20 | 805 | def _log_format(self): | 800 | def _log_format(self): |
21 | 806 | """See Config.log_format.""" | 801 | """See Config.log_format.""" |
22 | 807 | return self._get_user_option('log_format') | 802 | return self._get_user_option('log_format') |
23 | @@ -1314,10 +1309,6 @@ | |||
24 | 1314 | def remove_user_option(self, option_name, section_name=None): | 1309 | def remove_user_option(self, option_name, section_name=None): |
25 | 1315 | self._get_branch_data_config().remove_option(option_name, section_name) | 1310 | self._get_branch_data_config().remove_option(option_name, section_name) |
26 | 1316 | 1311 | ||
27 | 1317 | def _gpg_signing_command(self): | ||
28 | 1318 | """See Config.gpg_signing_command.""" | ||
29 | 1319 | return self._get_safe_value('_gpg_signing_command') | ||
30 | 1320 | |||
31 | 1321 | def _post_commit(self): | 1312 | def _post_commit(self): |
32 | 1322 | """See Config.post_commit.""" | 1313 | """See Config.post_commit.""" |
33 | 1323 | return self._get_safe_value('_post_commit') | 1314 | return self._get_safe_value('_post_commit') |
34 | @@ -2706,14 +2697,6 @@ | |||
35 | 2706 | Option('email', override_from_env=['BRZ_EMAIL'], default=default_email, | 2697 | Option('email', override_from_env=['BRZ_EMAIL'], default=default_email, |
36 | 2707 | help='The users identity')) | 2698 | help='The users identity')) |
37 | 2708 | option_registry.register( | 2699 | option_registry.register( |
38 | 2709 | Option('gpg_signing_command', | ||
39 | 2710 | default='gpg', | ||
40 | 2711 | help="""\ | ||
41 | 2712 | Program to use use for creating signatures. | ||
42 | 2713 | |||
43 | 2714 | This should support at least the -u and --clearsign options. | ||
44 | 2715 | """)) | ||
45 | 2716 | option_registry.register( | ||
46 | 2717 | Option('gpg_signing_key', | 2700 | Option('gpg_signing_key', |
47 | 2718 | default=None, | 2701 | default=None, |
48 | 2719 | help="""\ | 2702 | help="""\ |
49 | 2720 | 2703 | ||
50 | === modified file 'breezy/errors.py' | |||
51 | --- breezy/errors.py 2017-06-10 18:39:27 +0000 | |||
52 | +++ breezy/errors.py 2017-07-04 21:10:26 +0000 | |||
53 | @@ -1771,22 +1771,6 @@ | |||
54 | 1771 | self.username = username | 1771 | self.username = username |
55 | 1772 | 1772 | ||
56 | 1773 | 1773 | ||
57 | 1774 | class SigningFailed(BzrError): | ||
58 | 1775 | |||
59 | 1776 | _fmt = 'Failed to GPG sign data with command "%(command_line)s"' | ||
60 | 1777 | |||
61 | 1778 | def __init__(self, command_line): | ||
62 | 1779 | BzrError.__init__(self, command_line=command_line) | ||
63 | 1780 | |||
64 | 1781 | |||
65 | 1782 | class SignatureVerificationFailed(BzrError): | ||
66 | 1783 | |||
67 | 1784 | _fmt = 'Failed to verify GPG signature data with error "%(error)s"' | ||
68 | 1785 | |||
69 | 1786 | def __init__(self, error): | ||
70 | 1787 | BzrError.__init__(self, error=error) | ||
71 | 1788 | |||
72 | 1789 | |||
73 | 1790 | class DependencyNotPresent(BzrError): | 1774 | class DependencyNotPresent(BzrError): |
74 | 1791 | 1775 | ||
75 | 1792 | _fmt = 'Unable to import library "%(library)s": %(error)s' | 1776 | _fmt = 'Unable to import library "%(library)s": %(error)s' |
76 | @@ -1795,14 +1779,6 @@ | |||
77 | 1795 | BzrError.__init__(self, library=library, error=error) | 1779 | BzrError.__init__(self, library=library, error=error) |
78 | 1796 | 1780 | ||
79 | 1797 | 1781 | ||
80 | 1798 | class GpgmeNotInstalled(DependencyNotPresent): | ||
81 | 1799 | |||
82 | 1800 | _fmt = 'python-gpgme is not installed, it is needed to verify signatures' | ||
83 | 1801 | |||
84 | 1802 | def __init__(self, error): | ||
85 | 1803 | DependencyNotPresent.__init__(self, 'gpgme', error) | ||
86 | 1804 | |||
87 | 1805 | |||
88 | 1806 | class WorkingTreeNotRevision(BzrError): | 1782 | class WorkingTreeNotRevision(BzrError): |
89 | 1807 | 1783 | ||
90 | 1808 | _fmt = ("The working tree for %(basedir)s has changed since" | 1784 | _fmt = ("The working tree for %(basedir)s has changed since" |
91 | 1809 | 1785 | ||
92 | === modified file 'breezy/gpg.py' | |||
93 | --- breezy/gpg.py 2017-06-05 20:48:31 +0000 | |||
94 | +++ breezy/gpg.py 2017-07-04 21:10:26 +0000 | |||
95 | @@ -29,7 +29,6 @@ | |||
96 | 29 | 29 | ||
97 | 30 | from breezy import ( | 30 | from breezy import ( |
98 | 31 | config, | 31 | config, |
99 | 32 | errors, | ||
100 | 33 | trace, | 32 | trace, |
101 | 34 | ui, | 33 | ui, |
102 | 35 | ) | 34 | ) |
103 | @@ -39,6 +38,9 @@ | |||
104 | 39 | ) | 38 | ) |
105 | 40 | """) | 39 | """) |
106 | 41 | 40 | ||
107 | 41 | from . import ( | ||
108 | 42 | errors, | ||
109 | 43 | ) | ||
110 | 42 | from .sixish import ( | 44 | from .sixish import ( |
111 | 43 | BytesIO, | 45 | BytesIO, |
112 | 44 | ) | 46 | ) |
113 | @@ -51,6 +53,30 @@ | |||
114 | 51 | SIGNATURE_EXPIRED = 4 | 53 | SIGNATURE_EXPIRED = 4 |
115 | 52 | 54 | ||
116 | 53 | 55 | ||
117 | 56 | class GpgNotInstalled(errors.DependencyNotPresent): | ||
118 | 57 | |||
119 | 58 | _fmt = 'python-gpg is not installed, it is needed to verify signatures' | ||
120 | 59 | |||
121 | 60 | def __init__(self, error): | ||
122 | 61 | errors.DependencyNotPresent.__init__(self, 'gpg', error) | ||
123 | 62 | |||
124 | 63 | |||
125 | 64 | class SigningFailed(errors.BzrError): | ||
126 | 65 | |||
127 | 66 | _fmt = 'Failed to GPG sign data: "%(error)s"' | ||
128 | 67 | |||
129 | 68 | def __init__(self, error): | ||
130 | 69 | errors.BzrError.__init__(self, error=error) | ||
131 | 70 | |||
132 | 71 | |||
133 | 72 | class SignatureVerificationFailed(errors.BzrError): | ||
134 | 73 | |||
135 | 74 | _fmt = 'Failed to verify GPG signature data with error "%(error)s"' | ||
136 | 75 | |||
137 | 76 | def __init__(self, error): | ||
138 | 77 | errors.BzrError.__init__(self, error=error) | ||
139 | 78 | |||
140 | 79 | |||
141 | 54 | def bulk_verify_signatures(repository, revids, strategy, | 80 | def bulk_verify_signatures(repository, revids, strategy, |
142 | 55 | process_events_callback=None): | 81 | process_events_callback=None): |
143 | 56 | """Do verifications on a set of revisions | 82 | """Do verifications on a set of revisions |
144 | @@ -101,10 +127,10 @@ | |||
145 | 101 | """Real strategies take a configuration.""" | 127 | """Real strategies take a configuration.""" |
146 | 102 | 128 | ||
147 | 103 | def sign(self, content): | 129 | def sign(self, content): |
149 | 104 | raise errors.SigningFailed('Signing is disabled.') | 130 | raise SigningFailed('Signing is disabled.') |
150 | 105 | 131 | ||
151 | 106 | def verify(self, content, testament): | 132 | def verify(self, content, testament): |
153 | 107 | raise errors.SignatureVerificationFailed('Signature verification is \ | 133 | raise SignatureVerificationFailed('Signature verification is \ |
154 | 108 | disabled.') | 134 | disabled.') |
155 | 109 | 135 | ||
156 | 110 | def set_acceptable_keys(self, command_line_input): | 136 | def set_acceptable_keys(self, command_line_input): |
157 | @@ -162,11 +188,32 @@ | |||
158 | 162 | def __init__(self, config_stack): | 188 | def __init__(self, config_stack): |
159 | 163 | self._config_stack = config_stack | 189 | self._config_stack = config_stack |
160 | 164 | try: | 190 | try: |
163 | 165 | import gpgme | 191 | import gpg |
164 | 166 | self.context = gpgme.Context() | 192 | self.context = gpg.Context() |
165 | 167 | except ImportError as error: | 193 | except ImportError as error: |
166 | 168 | pass # can't use verify() | 194 | pass # can't use verify() |
167 | 169 | 195 | ||
168 | 196 | self.context.signers = self._get_signing_keys() | ||
169 | 197 | |||
170 | 198 | def _get_signing_keys(self): | ||
171 | 199 | import gpg | ||
172 | 200 | keyname = self._config_stack.get('gpg_signing_key') | ||
173 | 201 | if keyname: | ||
174 | 202 | try: | ||
175 | 203 | return [self.context.get_key(keyname)] | ||
176 | 204 | except gpg.errors.KeyNotFound: | ||
177 | 205 | pass | ||
178 | 206 | |||
179 | 207 | if keyname is None or keyname == 'default': | ||
180 | 208 | # 'default' or not setting gpg_signing_key at all means we should | ||
181 | 209 | # use the user email address | ||
182 | 210 | keyname = config.extract_email_address(self._config_stack.get('email')) | ||
183 | 211 | possible_keys = self.context.keylist(keyname, secret=True) | ||
184 | 212 | try: | ||
185 | 213 | return [possible_keys.next()] | ||
186 | 214 | except StopIteration: | ||
187 | 215 | return [] | ||
188 | 216 | |||
189 | 170 | @staticmethod | 217 | @staticmethod |
190 | 171 | def verify_signatures_available(): | 218 | def verify_signatures_available(): |
191 | 172 | """ | 219 | """ |
192 | @@ -175,55 +222,24 @@ | |||
193 | 175 | :return: boolean if this strategy can verify signatures | 222 | :return: boolean if this strategy can verify signatures |
194 | 176 | """ | 223 | """ |
195 | 177 | try: | 224 | try: |
197 | 178 | import gpgme | 225 | import gpg |
198 | 179 | return True | 226 | return True |
199 | 180 | except ImportError as error: | 227 | except ImportError as error: |
200 | 181 | return False | 228 | return False |
201 | 182 | 229 | ||
202 | 183 | def _command_line(self): | ||
203 | 184 | key = self._config_stack.get('gpg_signing_key') | ||
204 | 185 | if key is None or key == 'default': | ||
205 | 186 | # 'default' or not setting gpg_signing_key at all means we should | ||
206 | 187 | # use the user email address | ||
207 | 188 | key = config.extract_email_address(self._config_stack.get('email')) | ||
208 | 189 | return [self._config_stack.get('gpg_signing_command'), '--clearsign', | ||
209 | 190 | '-u', key] | ||
210 | 191 | |||
211 | 192 | def sign(self, content): | 230 | def sign(self, content): |
212 | 231 | import gpg | ||
213 | 193 | if isinstance(content, unicode): | 232 | if isinstance(content, unicode): |
214 | 194 | raise errors.BzrBadParameterUnicode('content') | 233 | raise errors.BzrBadParameterUnicode('content') |
215 | 195 | ui.ui_factory.clear_term() | ||
216 | 196 | 234 | ||
221 | 197 | preexec_fn = _set_gpg_tty | 235 | plain_text = gpg.Data(content) |
218 | 198 | if sys.platform == 'win32': | ||
219 | 199 | # Win32 doesn't support preexec_fn, but wouldn't support TTY anyway. | ||
220 | 200 | preexec_fn = None | ||
222 | 201 | try: | 236 | try: |
248 | 202 | process = subprocess.Popen(self._command_line(), | 237 | output, result = self.context.sign( |
249 | 203 | stdin=subprocess.PIPE, | 238 | plain_text, mode=gpg.constants.sig.mode.CLEAR) |
250 | 204 | stdout=subprocess.PIPE, | 239 | except gpg.errors.GPGMEError as error: |
251 | 205 | preexec_fn=preexec_fn) | 240 | raise SigningFailed(str(error)) |
252 | 206 | try: | 241 | |
253 | 207 | result = process.communicate(content)[0] | 242 | return output |
229 | 208 | if process.returncode is None: | ||
230 | 209 | process.wait() | ||
231 | 210 | if process.returncode != 0: | ||
232 | 211 | raise errors.SigningFailed(self._command_line()) | ||
233 | 212 | return result | ||
234 | 213 | except OSError as e: | ||
235 | 214 | if e.errno == errno.EPIPE: | ||
236 | 215 | raise errors.SigningFailed(self._command_line()) | ||
237 | 216 | else: | ||
238 | 217 | raise | ||
239 | 218 | except ValueError: | ||
240 | 219 | # bad subprocess parameters, should never happen. | ||
241 | 220 | raise | ||
242 | 221 | except OSError as e: | ||
243 | 222 | if e.errno == errno.ENOENT: | ||
244 | 223 | # gpg is not installed | ||
245 | 224 | raise errors.SigningFailed(self._command_line()) | ||
246 | 225 | else: | ||
247 | 226 | raise | ||
254 | 227 | 243 | ||
255 | 228 | def verify(self, content, testament): | 244 | def verify(self, content, testament): |
256 | 229 | """Check content has a valid signature. | 245 | """Check content has a valid signature. |
257 | @@ -234,74 +250,74 @@ | |||
258 | 234 | :return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid | 250 | :return: SIGNATURE_VALID or a failed SIGNATURE_ value, key uid if valid |
259 | 235 | """ | 251 | """ |
260 | 236 | try: | 252 | try: |
262 | 237 | import gpgme | 253 | import gpg |
263 | 238 | except ImportError as error: | 254 | except ImportError as error: |
265 | 239 | raise errors.GpgmeNotInstalled(error) | 255 | raise errors.GpgNotInstalled(error) |
266 | 240 | 256 | ||
269 | 241 | signature = BytesIO(content) | 257 | signature = gpg.Data(content) |
270 | 242 | plain_output = BytesIO() | 258 | sink = gpg.Data() |
271 | 243 | try: | 259 | try: |
275 | 244 | result = self.context.verify(signature, None, plain_output) | 260 | plain_output, result = self.context.verify(signature) |
276 | 245 | except gpgme.GpgmeError as error: | 261 | except gpg.errors.BadSignatures as error: |
277 | 246 | raise errors.SignatureVerificationFailed(error[2]) | 262 | fingerprint = error.result.signatures[0].fpr |
278 | 263 | if error.result.signatures[0].summary & gpg.constants.SIGSUM_KEY_EXPIRED: | ||
279 | 264 | expires = self.context.get_key(error.result.signatures[0].fpr).subkeys[0].expires | ||
280 | 265 | if expires > error.result.signatures[0].timestamp: | ||
281 | 266 | # The expired key was not expired at time of signing. | ||
282 | 267 | # test_verify_expired_but_valid() | ||
283 | 268 | return SIGNATURE_EXPIRED, fingerprint[-8:] | ||
284 | 269 | else: | ||
285 | 270 | # I can't work out how to create a test where the signature | ||
286 | 271 | # was expired at the time of signing. | ||
287 | 272 | return SIGNATURE_NOT_VALID, None | ||
288 | 273 | |||
289 | 274 | # GPG does not know this key. | ||
290 | 275 | # test_verify_unknown_key() | ||
291 | 276 | if error.result.signatures[0].summary & gpg.constants.SIGSUM_KEY_MISSING: | ||
292 | 277 | return SIGNATURE_KEY_MISSING, fingerprint[-8:] | ||
293 | 278 | |||
294 | 279 | return SIGNATURE_NOT_VALID, None | ||
295 | 280 | except gpg.errors.GPGMEError as error: | ||
296 | 281 | raise SignatureVerificationFailed(error[2]) | ||
297 | 247 | 282 | ||
298 | 248 | # No result if input is invalid. | 283 | # No result if input is invalid. |
299 | 249 | # test_verify_invalid() | 284 | # test_verify_invalid() |
301 | 250 | if len(result) == 0: | 285 | if len(result.signatures) == 0: |
302 | 251 | return SIGNATURE_NOT_VALID, None | 286 | return SIGNATURE_NOT_VALID, None |
303 | 252 | # User has specified a list of acceptable keys, check our result is in | 287 | # User has specified a list of acceptable keys, check our result is in |
304 | 253 | # it. test_verify_unacceptable_key() | 288 | # it. test_verify_unacceptable_key() |
306 | 254 | fingerprint = result[0].fpr | 289 | fingerprint = result.signatures[0].fpr |
307 | 255 | if self.acceptable_keys is not None: | 290 | if self.acceptable_keys is not None: |
308 | 256 | if not fingerprint in self.acceptable_keys: | 291 | if not fingerprint in self.acceptable_keys: |
309 | 257 | return SIGNATURE_KEY_MISSING, fingerprint[-8:] | 292 | return SIGNATURE_KEY_MISSING, fingerprint[-8:] |
310 | 258 | # Check the signature actually matches the testament. | 293 | # Check the signature actually matches the testament. |
311 | 259 | # test_verify_bad_testament() | 294 | # test_verify_bad_testament() |
313 | 260 | if testament != plain_output.getvalue(): | 295 | if testament != plain_output: |
314 | 261 | return SIGNATURE_NOT_VALID, None | 296 | return SIGNATURE_NOT_VALID, None |
316 | 262 | # Yay gpgme set the valid bit. | 297 | # Yay gpg set the valid bit. |
317 | 263 | # Can't write a test for this one as you can't set a key to be | 298 | # Can't write a test for this one as you can't set a key to be |
320 | 264 | # trusted using gpgme. | 299 | # trusted using gpg. |
321 | 265 | if result[0].summary & gpgme.SIGSUM_VALID: | 300 | if result.signatures[0].summary & gpg.constants.SIGSUM_VALID: |
322 | 266 | key = self.context.get_key(fingerprint) | 301 | key = self.context.get_key(fingerprint) |
323 | 267 | name = key.uids[0].name | 302 | name = key.uids[0].name |
324 | 268 | email = key.uids[0].email | 303 | email = key.uids[0].email |
325 | 269 | return SIGNATURE_VALID, name + " <" + email + ">" | 304 | return SIGNATURE_VALID, name + " <" + email + ">" |
326 | 270 | # Sigsum_red indicates a problem, unfortunatly I have not been able | 305 | # Sigsum_red indicates a problem, unfortunatly I have not been able |
327 | 271 | # to write any tests which actually set this. | 306 | # to write any tests which actually set this. |
329 | 272 | if result[0].summary & gpgme.SIGSUM_RED: | 307 | if result.signatures[0].summary & gpg.constants.SIGSUM_RED: |
330 | 273 | return SIGNATURE_NOT_VALID, None | 308 | return SIGNATURE_NOT_VALID, None |
331 | 274 | # GPG does not know this key. | ||
332 | 275 | # test_verify_unknown_key() | ||
333 | 276 | if result[0].summary & gpgme.SIGSUM_KEY_MISSING: | ||
334 | 277 | return SIGNATURE_KEY_MISSING, fingerprint[-8:] | ||
335 | 278 | # Summary isn't set if sig is valid but key is untrusted but if user | 309 | # Summary isn't set if sig is valid but key is untrusted but if user |
336 | 279 | # has explicity set the key as acceptable we can validate it. | 310 | # has explicity set the key as acceptable we can validate it. |
338 | 280 | if result[0].summary == 0 and self.acceptable_keys is not None: | 311 | if result.signatures[0].summary == 0 and self.acceptable_keys is not None: |
339 | 281 | if fingerprint in self.acceptable_keys: | 312 | if fingerprint in self.acceptable_keys: |
340 | 282 | # test_verify_untrusted_but_accepted() | 313 | # test_verify_untrusted_but_accepted() |
341 | 283 | return SIGNATURE_VALID, None | 314 | return SIGNATURE_VALID, None |
342 | 284 | # test_verify_valid_but_untrusted() | 315 | # test_verify_valid_but_untrusted() |
359 | 285 | if result[0].summary == 0 and self.acceptable_keys is None: | 316 | if result.signatures[0].summary == 0 and self.acceptable_keys is None: |
344 | 286 | return SIGNATURE_NOT_VALID, None | ||
345 | 287 | if result[0].summary & gpgme.SIGSUM_KEY_EXPIRED: | ||
346 | 288 | expires = self.context.get_key(result[0].fpr).subkeys[0].expires | ||
347 | 289 | if expires > result[0].timestamp: | ||
348 | 290 | # The expired key was not expired at time of signing. | ||
349 | 291 | # test_verify_expired_but_valid() | ||
350 | 292 | return SIGNATURE_EXPIRED, fingerprint[-8:] | ||
351 | 293 | else: | ||
352 | 294 | # I can't work out how to create a test where the signature | ||
353 | 295 | # was expired at the time of signing. | ||
354 | 296 | return SIGNATURE_NOT_VALID, None | ||
355 | 297 | # A signature from a revoked key gets this. | ||
356 | 298 | # test_verify_revoked_signature() | ||
357 | 299 | if ((result[0].summary & gpgme.SIGSUM_SYS_ERROR | ||
358 | 300 | or result[0].status.strerror == 'Certificate revoked')): | ||
360 | 301 | return SIGNATURE_NOT_VALID, None | 317 | return SIGNATURE_NOT_VALID, None |
361 | 302 | # Other error types such as revoked keys should (I think) be caught by | 318 | # Other error types such as revoked keys should (I think) be caught by |
362 | 303 | # SIGSUM_RED so anything else means something is buggy. | 319 | # SIGSUM_RED so anything else means something is buggy. |
364 | 304 | raise errors.SignatureVerificationFailed( | 320 | raise SignatureVerificationFailed( |
365 | 305 | "Unknown GnuPG key verification result") | 321 | "Unknown GnuPG key verification result") |
366 | 306 | 322 | ||
367 | 307 | def set_acceptable_keys(self, command_line_input): | 323 | def set_acceptable_keys(self, command_line_input): |
368 | 308 | 324 | ||
369 | === modified file 'breezy/help_topics/en/configuration.txt' | |||
370 | --- breezy/help_topics/en/configuration.txt 2017-06-12 23:25:04 +0000 | |||
371 | +++ breezy/help_topics/en/configuration.txt 2017-07-04 21:10:26 +0000 | |||
372 | @@ -498,16 +498,6 @@ | |||
373 | 498 | This section only applies to the branch at this directory and not | 498 | This section only applies to the branch at this directory and not |
374 | 499 | branches below it. | 499 | branches below it. |
375 | 500 | 500 | ||
376 | 501 | gpg_signing_command | ||
377 | 502 | ~~~~~~~~~~~~~~~~~~~ | ||
378 | 503 | |||
379 | 504 | (Default: "gpg"). Which program should be used to sign and check revisions. | ||
380 | 505 | For example:: | ||
381 | 506 | |||
382 | 507 | gpg_signing_command = /usr/bin/gnpg | ||
383 | 508 | |||
384 | 509 | The specified command must accept the options "--clearsign" and "-u <email>". | ||
385 | 510 | |||
386 | 511 | bzr_remote_path | 501 | bzr_remote_path |
387 | 512 | ~~~~~~~~~~~~~~~ | 502 | ~~~~~~~~~~~~~~~ |
388 | 513 | 503 | ||
389 | 514 | 504 | ||
390 | === modified file 'breezy/tests/blackbox/test_log.py' | |||
391 | --- breezy/tests/blackbox/test_log.py 2017-06-22 01:52:28 +0000 | |||
392 | +++ breezy/tests/blackbox/test_log.py 2017-07-04 21:10:26 +0000 | |||
393 | @@ -17,6 +17,9 @@ | |||
394 | 17 | 17 | ||
395 | 18 | """Black-box tests for brz log.""" | 18 | """Black-box tests for brz log.""" |
396 | 19 | 19 | ||
397 | 20 | from __future__ import absolute_import | ||
398 | 21 | |||
399 | 22 | |||
400 | 20 | import os | 23 | import os |
401 | 21 | 24 | ||
402 | 22 | from breezy import ( | 25 | from breezy import ( |
403 | @@ -474,7 +477,7 @@ | |||
404 | 474 | class TestLogSignatures(TestLog): | 477 | class TestLogSignatures(TestLog): |
405 | 475 | 478 | ||
406 | 476 | def test_log_with_signatures(self): | 479 | def test_log_with_signatures(self): |
408 | 477 | self.requireFeature(features.gpgme) | 480 | self.requireFeature(features.gpg) |
409 | 478 | 481 | ||
410 | 479 | tree = self.make_linear_branch(format='dirstate-tags') | 482 | tree = self.make_linear_branch(format='dirstate-tags') |
411 | 480 | 483 | ||
412 | @@ -482,7 +485,7 @@ | |||
413 | 482 | self.assertTrue('signature: no signature' in log) | 485 | self.assertTrue('signature: no signature' in log) |
414 | 483 | 486 | ||
415 | 484 | def test_log_without_signatures(self): | 487 | def test_log_without_signatures(self): |
417 | 485 | self.requireFeature(features.gpgme) | 488 | self.requireFeature(features.gpg) |
418 | 486 | 489 | ||
419 | 487 | tree = self.make_linear_branch(format='dirstate-tags') | 490 | tree = self.make_linear_branch(format='dirstate-tags') |
420 | 488 | 491 | ||
421 | 489 | 492 | ||
422 | === modified file 'breezy/tests/features.py' | |||
423 | --- breezy/tests/features.py 2017-06-17 12:58:29 +0000 | |||
424 | +++ breezy/tests/features.py 2017-07-04 21:10:26 +0000 | |||
425 | @@ -17,6 +17,8 @@ | |||
426 | 17 | """A collection of commonly used 'Features' to optionally run tests. | 17 | """A collection of commonly used 'Features' to optionally run tests. |
427 | 18 | """ | 18 | """ |
428 | 19 | 19 | ||
429 | 20 | from __future__ import absolute_import | ||
430 | 21 | |||
431 | 20 | import os | 22 | import os |
432 | 21 | import subprocess | 23 | import subprocess |
433 | 22 | import stat | 24 | import stat |
434 | @@ -373,7 +375,7 @@ | |||
435 | 373 | not_running_as_root = _NotRunningAsRoot() | 375 | not_running_as_root = _NotRunningAsRoot() |
436 | 374 | 376 | ||
437 | 375 | apport = ModuleAvailableFeature('apport') | 377 | apport = ModuleAvailableFeature('apport') |
439 | 376 | gpgme = ModuleAvailableFeature('gpgme') | 378 | gpg = ModuleAvailableFeature('gpg') |
440 | 377 | lzma = ModuleAvailableFeature('lzma') | 379 | lzma = ModuleAvailableFeature('lzma') |
441 | 378 | meliae = ModuleAvailableFeature('meliae.scanner') | 380 | meliae = ModuleAvailableFeature('meliae.scanner') |
442 | 379 | paramiko = ModuleAvailableFeature('paramiko') | 381 | paramiko = ModuleAvailableFeature('paramiko') |
443 | 380 | 382 | ||
444 | === modified file 'breezy/tests/test_commit.py' | |||
445 | --- breezy/tests/test_commit.py 2017-06-18 22:23:02 +0000 | |||
446 | +++ breezy/tests/test_commit.py 2017-07-04 21:10:26 +0000 | |||
447 | @@ -33,7 +33,6 @@ | |||
448 | 33 | from ..errors import ( | 33 | from ..errors import ( |
449 | 34 | PointlessCommit, | 34 | PointlessCommit, |
450 | 35 | BzrError, | 35 | BzrError, |
451 | 36 | SigningFailed, | ||
452 | 37 | LockContention, | 36 | LockContention, |
453 | 38 | ) | 37 | ) |
454 | 39 | from . import ( | 38 | from . import ( |
455 | @@ -53,7 +52,6 @@ | |||
456 | 53 | 52 | ||
457 | 54 | def __init__(self): | 53 | def __init__(self): |
458 | 55 | super(MustSignConfig, self).__init__(''' | 54 | super(MustSignConfig, self).__init__(''' |
459 | 56 | gpg_signing_command=cat - | ||
460 | 57 | create_signatures=always | 55 | create_signatures=always |
461 | 58 | ''') | 56 | ''') |
462 | 59 | 57 | ||
463 | @@ -431,7 +429,6 @@ | |||
464 | 431 | # monkey patch gpg signing mechanism | 429 | # monkey patch gpg signing mechanism |
465 | 432 | breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy | 430 | breezy.gpg.GPGStrategy = breezy.gpg.LoopbackGPGStrategy |
466 | 433 | conf = config.MemoryStack(''' | 431 | conf = config.MemoryStack(''' |
467 | 434 | gpg_signing_command=cat - | ||
468 | 435 | create_signatures=always | 432 | create_signatures=always |
469 | 436 | ''') | 433 | ''') |
470 | 437 | commit.Commit(config_stack=conf).commit( | 434 | commit.Commit(config_stack=conf).commit( |
471 | @@ -457,10 +454,9 @@ | |||
472 | 457 | # monkey patch gpg signing mechanism | 454 | # monkey patch gpg signing mechanism |
473 | 458 | breezy.gpg.GPGStrategy = breezy.gpg.DisabledGPGStrategy | 455 | breezy.gpg.GPGStrategy = breezy.gpg.DisabledGPGStrategy |
474 | 459 | conf = config.MemoryStack(''' | 456 | conf = config.MemoryStack(''' |
475 | 460 | gpg_signing_command=cat - | ||
476 | 461 | create_signatures=always | 457 | create_signatures=always |
477 | 462 | ''') | 458 | ''') |
479 | 463 | self.assertRaises(SigningFailed, | 459 | self.assertRaises(breezy.gpg.SigningFailed, |
480 | 464 | commit.Commit(config_stack=conf).commit, | 460 | commit.Commit(config_stack=conf).commit, |
481 | 465 | message="base", | 461 | message="base", |
482 | 466 | allow_pointless=True, | 462 | allow_pointless=True, |
483 | 467 | 463 | ||
484 | === modified file 'breezy/tests/test_config.py' | |||
485 | --- breezy/tests/test_config.py 2017-06-12 23:25:04 +0000 | |||
486 | +++ breezy/tests/test_config.py 2017-07-04 21:10:26 +0000 | |||
487 | @@ -176,7 +176,6 @@ | |||
488 | 176 | email=Erik B\u00e5gfors <erik@bagfors.nu> | 176 | email=Erik B\u00e5gfors <erik@bagfors.nu> |
489 | 177 | editor=vim | 177 | editor=vim |
490 | 178 | change_editor=vimdiff -of @new_path @old_path | 178 | change_editor=vimdiff -of @new_path @old_path |
491 | 179 | gpg_signing_command=gnome-gpg | ||
492 | 180 | gpg_signing_key=DD4D5088 | 179 | gpg_signing_key=DD4D5088 |
493 | 181 | log_format=short | 180 | log_format=short |
494 | 182 | validate_signatures_in_log=true | 181 | validate_signatures_in_log=true |
495 | @@ -232,7 +231,6 @@ | |||
496 | 232 | # test trailing / matching with no children | 231 | # test trailing / matching with no children |
497 | 233 | [/a/] | 232 | [/a/] |
498 | 234 | check_signatures=check-available | 233 | check_signatures=check-available |
499 | 235 | gpg_signing_command=false | ||
500 | 236 | gpg_signing_key=default | 234 | gpg_signing_key=default |
501 | 237 | user_local_option=local | 235 | user_local_option=local |
502 | 238 | # test trailing / matching | 236 | # test trailing / matching |
503 | 239 | 237 | ||
504 | === modified file 'breezy/tests/test_gpg.py' | |||
505 | --- breezy/tests/test_gpg.py 2017-05-22 00:56:52 +0000 | |||
506 | +++ breezy/tests/test_gpg.py 2017-07-04 21:10:26 +0000 | |||
507 | @@ -43,90 +43,17 @@ | |||
508 | 43 | if content is None: | 43 | if content is None: |
509 | 44 | content = ''' | 44 | content = ''' |
510 | 45 | gpg_signing_key=amy@example.com | 45 | gpg_signing_key=amy@example.com |
512 | 46 | gpg_signing_command=false''' | 46 | ''' |
513 | 47 | super(FakeConfig, self).__init__(content) | 47 | super(FakeConfig, self).__init__(content) |
514 | 48 | 48 | ||
515 | 49 | 49 | ||
516 | 50 | class TestCommandLine(tests.TestCase): | ||
517 | 51 | |||
518 | 52 | def setUp(self): | ||
519 | 53 | super(TestCommandLine, self).setUp() | ||
520 | 54 | self.my_gpg = gpg.GPGStrategy(FakeConfig()) | ||
521 | 55 | |||
522 | 56 | def test_signing_command_line(self): | ||
523 | 57 | self.assertEqual(['false', '--clearsign', '-u', 'amy@example.com'], | ||
524 | 58 | self.my_gpg._command_line()) | ||
525 | 59 | |||
526 | 60 | def test_signing_command_line_from_default(self): | ||
527 | 61 | # Using 'default' for gpg_signing_key will use the mail part of 'email' | ||
528 | 62 | my_gpg = gpg.GPGStrategy(FakeConfig(''' | ||
529 | 63 | email=Amy <amy@example.com> | ||
530 | 64 | gpg_signing_key=default | ||
531 | 65 | gpg_signing_command=false''')) | ||
532 | 66 | self.assertEqual(['false', '--clearsign', '-u', 'amy@example.com'], | ||
533 | 67 | my_gpg._command_line()) | ||
534 | 68 | |||
535 | 69 | def test_signing_command_line_from_email(self): | ||
536 | 70 | # Not setting gpg_signing_key will use the mail part of 'email' | ||
537 | 71 | my_gpg = gpg.GPGStrategy(FakeConfig(''' | ||
538 | 72 | email=Amy <amy@example.com> | ||
539 | 73 | gpg_signing_command=false''')) | ||
540 | 74 | self.assertEqual(['false', '--clearsign', '-u', 'amy@example.com'], | ||
541 | 75 | my_gpg._command_line()) | ||
542 | 76 | |||
543 | 77 | def test_checks_return_code(self): | ||
544 | 78 | # This test needs a unix like platform - one with 'false' to run. | ||
545 | 79 | # if you have one, please make this work :) | ||
546 | 80 | self.assertRaises(errors.SigningFailed, self.my_gpg.sign, 'content') | ||
547 | 81 | |||
548 | 82 | def assertProduces(self, content): | ||
549 | 83 | # This needs a 'cat' command or similar to work. | ||
550 | 84 | if sys.platform == 'win32': | ||
551 | 85 | # Windows doesn't come with cat, and we don't require it | ||
552 | 86 | # so lets try using python instead. | ||
553 | 87 | # But stupid windows and line-ending conversions. | ||
554 | 88 | # It is too much work to make sys.stdout be in binary mode. | ||
555 | 89 | # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/65443 | ||
556 | 90 | self.my_gpg._command_line = lambda:[sys.executable, '-c', | ||
557 | 91 | 'import sys; sys.stdout.write(sys.stdin.read())'] | ||
558 | 92 | new_content = content.replace('\n', '\r\n') | ||
559 | 93 | |||
560 | 94 | self.assertEqual(new_content, self.my_gpg.sign(content)) | ||
561 | 95 | else: | ||
562 | 96 | self.my_gpg._command_line = lambda:['cat', '-'] | ||
563 | 97 | self.assertEqual(content, self.my_gpg.sign(content)) | ||
564 | 98 | |||
565 | 99 | def test_returns_output(self): | ||
566 | 100 | content = "some content\nwith newlines\n" | ||
567 | 101 | self.assertProduces(content) | ||
568 | 102 | |||
569 | 103 | def test_clears_progress(self): | ||
570 | 104 | content = "some content\nwith newlines\n" | ||
571 | 105 | old_clear_term = ui.ui_factory.clear_term | ||
572 | 106 | clear_term_called = [] | ||
573 | 107 | def clear_term(): | ||
574 | 108 | old_clear_term() | ||
575 | 109 | clear_term_called.append(True) | ||
576 | 110 | ui.ui_factory.clear_term = clear_term | ||
577 | 111 | try: | ||
578 | 112 | self.assertProduces(content) | ||
579 | 113 | finally: | ||
580 | 114 | ui.ui_factory.clear_term = old_clear_term | ||
581 | 115 | self.assertEqual([True], clear_term_called) | ||
582 | 116 | |||
583 | 117 | def test_aborts_on_unicode(self): | ||
584 | 118 | """You can't sign Unicode text; it must be encoded first.""" | ||
585 | 119 | self.assertRaises(errors.BzrBadParameterUnicode, | ||
586 | 120 | self.assertProduces, u'foo') | ||
587 | 121 | |||
588 | 122 | |||
589 | 123 | class TestVerify(TestCase): | 50 | class TestVerify(TestCase): |
590 | 124 | 51 | ||
591 | 125 | def import_keys(self): | 52 | def import_keys(self): |
594 | 126 | import gpgme | 53 | import gpg |
595 | 127 | context = gpgme.Context() | 54 | context = gpg.Context() |
596 | 128 | 55 | ||
598 | 129 | key = BytesIO(b"""-----BEGIN PGP PUBLIC KEY BLOCK----- | 56 | key = gpg.Data(b"""-----BEGIN PGP PUBLIC KEY BLOCK----- |
599 | 130 | Version: GnuPG v1.4.11 (GNU/Linux) | 57 | Version: GnuPG v1.4.11 (GNU/Linux) |
600 | 131 | 58 | ||
601 | 132 | mQENBE343IgBCADwzPW7kmKb2bjB+UU+1ER/ABMZspvtoZMPusUw7bk6coXHF/0W | 59 | mQENBE343IgBCADwzPW7kmKb2bjB+UU+1ER/ABMZspvtoZMPusUw7bk6coXHF/0W |
602 | @@ -158,7 +85,7 @@ | |||
603 | 158 | -----END PGP PUBLIC KEY BLOCK----- | 85 | -----END PGP PUBLIC KEY BLOCK----- |
604 | 159 | """) | 86 | """) |
605 | 160 | 87 | ||
607 | 161 | secret_key = BytesIO(b"""-----BEGIN PGP PRIVATE KEY BLOCK----- | 88 | secret_key = gpg.Data(b"""-----BEGIN PGP PRIVATE KEY BLOCK----- |
608 | 162 | Version: GnuPG v1.4.11 (GNU/Linux) | 89 | Version: GnuPG v1.4.11 (GNU/Linux) |
609 | 163 | 90 | ||
610 | 164 | lQOYBE343IgBCADwzPW7kmKb2bjB+UU+1ER/ABMZspvtoZMPusUw7bk6coXHF/0W | 91 | lQOYBE343IgBCADwzPW7kmKb2bjB+UU+1ER/ABMZspvtoZMPusUw7bk6coXHF/0W |
611 | @@ -217,7 +144,7 @@ | |||
612 | 217 | -----END PGP PRIVATE KEY BLOCK----- | 144 | -----END PGP PRIVATE KEY BLOCK----- |
613 | 218 | """) | 145 | """) |
614 | 219 | 146 | ||
616 | 220 | revoked_key = BytesIO(b"""-----BEGIN PGP PUBLIC KEY BLOCK----- | 147 | revoked_key = gpg.Data(b"""-----BEGIN PGP PUBLIC KEY BLOCK----- |
617 | 221 | Version: GnuPG v1.4.11 (GNU/Linux) | 148 | Version: GnuPG v1.4.11 (GNU/Linux) |
618 | 222 | 149 | ||
619 | 223 | mI0ETjlW5gEEAOb/6P+TVM59E897wRtatxys2BhsHCXM4T7xjIiANfDwejDdifqh | 150 | mI0ETjlW5gEEAOb/6P+TVM59E897wRtatxys2BhsHCXM4T7xjIiANfDwejDdifqh |
620 | @@ -242,7 +169,7 @@ | |||
621 | 242 | -----END PGP PUBLIC KEY BLOCK----- | 169 | -----END PGP PUBLIC KEY BLOCK----- |
622 | 243 | """) | 170 | """) |
623 | 244 | 171 | ||
625 | 245 | expired_key = BytesIO(b"""-----BEGIN PGP PUBLIC KEY BLOCK----- | 172 | expired_key = gpg.Data(b"""-----BEGIN PGP PUBLIC KEY BLOCK----- |
626 | 246 | Version: GnuPG v1.4.11 (GNU/Linux) | 173 | Version: GnuPG v1.4.11 (GNU/Linux) |
627 | 247 | 174 | ||
628 | 248 | mI0ETjZ6PAEEALkR4GcFQidCCxV7pgQwQd5MZua0YO2l92fVqHX+PhnZ6egCLKdD | 175 | mI0ETjZ6PAEEALkR4GcFQidCCxV7pgQwQd5MZua0YO2l92fVqHX+PhnZ6egCLKdD |
629 | @@ -263,14 +190,14 @@ | |||
630 | 263 | =p0gt | 190 | =p0gt |
631 | 264 | -----END PGP PUBLIC KEY BLOCK----- | 191 | -----END PGP PUBLIC KEY BLOCK----- |
632 | 265 | """) | 192 | """) |
637 | 266 | context.import_(key) | 193 | context.op_import(key) |
638 | 267 | context.import_(secret_key) | 194 | context.op_import(secret_key) |
639 | 268 | context.import_(revoked_key) | 195 | context.op_import(revoked_key) |
640 | 269 | context.import_(expired_key) | 196 | context.op_import(expired_key) |
641 | 270 | 197 | ||
642 | 271 | def test_verify_untrusted_but_accepted(self): | 198 | def test_verify_untrusted_but_accepted(self): |
643 | 272 | #untrusted by gpg but listed as acceptable_keys by user | 199 | #untrusted by gpg but listed as acceptable_keys by user |
645 | 273 | self.requireFeature(features.gpgme) | 200 | self.requireFeature(features.gpg) |
646 | 274 | self.import_keys() | 201 | self.import_keys() |
647 | 275 | 202 | ||
648 | 276 | content = """-----BEGIN PGP SIGNED MESSAGE----- | 203 | content = """-----BEGIN PGP SIGNED MESSAGE----- |
649 | @@ -301,7 +228,7 @@ | |||
650 | 301 | plain)) | 228 | plain)) |
651 | 302 | 229 | ||
652 | 303 | def test_verify_unacceptable_key(self): | 230 | def test_verify_unacceptable_key(self): |
654 | 304 | self.requireFeature(features.gpgme) | 231 | self.requireFeature(features.gpg) |
655 | 305 | self.import_keys() | 232 | self.import_keys() |
656 | 306 | 233 | ||
657 | 307 | content = """-----BEGIN PGP SIGNED MESSAGE----- | 234 | content = """-----BEGIN PGP SIGNED MESSAGE----- |
658 | @@ -332,7 +259,7 @@ | |||
659 | 332 | my_gpg.verify(content, plain)) | 259 | my_gpg.verify(content, plain)) |
660 | 333 | 260 | ||
661 | 334 | def test_verify_valid_but_untrusted(self): | 261 | def test_verify_valid_but_untrusted(self): |
663 | 335 | self.requireFeature(features.gpgme) | 262 | self.requireFeature(features.gpg) |
664 | 336 | self.import_keys() | 263 | self.import_keys() |
665 | 337 | 264 | ||
666 | 338 | content = """-----BEGIN PGP SIGNED MESSAGE----- | 265 | content = """-----BEGIN PGP SIGNED MESSAGE----- |
667 | @@ -362,7 +289,7 @@ | |||
668 | 362 | plain)) | 289 | plain)) |
669 | 363 | 290 | ||
670 | 364 | def test_verify_bad_testament(self): | 291 | def test_verify_bad_testament(self): |
672 | 365 | self.requireFeature(features.gpgme) | 292 | self.requireFeature(features.gpg) |
673 | 366 | self.import_keys() | 293 | self.import_keys() |
674 | 367 | 294 | ||
675 | 368 | content = """-----BEGIN PGP SIGNED MESSAGE----- | 295 | content = """-----BEGIN PGP SIGNED MESSAGE----- |
676 | @@ -394,7 +321,7 @@ | |||
677 | 394 | 321 | ||
678 | 395 | 322 | ||
679 | 396 | def test_verify_revoked_signature(self): | 323 | def test_verify_revoked_signature(self): |
681 | 397 | self.requireFeature(features.gpgme) | 324 | self.requireFeature(features.gpg) |
682 | 398 | self.import_keys() | 325 | self.import_keys() |
683 | 399 | 326 | ||
684 | 400 | content = """-----BEGIN PGP SIGNED MESSAGE----- | 327 | content = """-----BEGIN PGP SIGNED MESSAGE----- |
685 | @@ -418,7 +345,7 @@ | |||
686 | 418 | plain)) | 345 | plain)) |
687 | 419 | 346 | ||
688 | 420 | def test_verify_invalid(self): | 347 | def test_verify_invalid(self): |
690 | 421 | self.requireFeature(features.gpgme) | 348 | self.requireFeature(features.gpg) |
691 | 422 | self.import_keys() | 349 | self.import_keys() |
692 | 423 | content = """-----BEGIN PGP SIGNED MESSAGE----- | 350 | content = """-----BEGIN PGP SIGNED MESSAGE----- |
693 | 424 | Hash: SHA1 | 351 | Hash: SHA1 |
694 | @@ -443,7 +370,7 @@ | |||
695 | 443 | my_gpg.verify(content, plain)) | 370 | my_gpg.verify(content, plain)) |
696 | 444 | 371 | ||
697 | 445 | def test_verify_expired_but_valid(self): | 372 | def test_verify_expired_but_valid(self): |
699 | 446 | self.requireFeature(features.gpgme) | 373 | self.requireFeature(features.gpg) |
700 | 447 | self.import_keys() | 374 | self.import_keys() |
701 | 448 | content = """-----BEGIN PGP SIGNED MESSAGE----- | 375 | content = """-----BEGIN PGP SIGNED MESSAGE----- |
702 | 449 | Hash: SHA1 | 376 | Hash: SHA1 |
703 | @@ -470,7 +397,7 @@ | |||
704 | 470 | my_gpg.verify(content, plain)) | 397 | my_gpg.verify(content, plain)) |
705 | 471 | 398 | ||
706 | 472 | def test_verify_unknown_key(self): | 399 | def test_verify_unknown_key(self): |
708 | 473 | self.requireFeature(features.gpgme) | 400 | self.requireFeature(features.gpg) |
709 | 474 | self.import_keys() | 401 | self.import_keys() |
710 | 475 | content = """-----BEGIN PGP SIGNED MESSAGE----- | 402 | content = """-----BEGIN PGP SIGNED MESSAGE----- |
711 | 476 | Hash: SHA1 | 403 | Hash: SHA1 |
712 | @@ -494,7 +421,7 @@ | |||
713 | 494 | my_gpg.verify(content, plain)) | 421 | my_gpg.verify(content, plain)) |
714 | 495 | 422 | ||
715 | 496 | def test_set_acceptable_keys(self): | 423 | def test_set_acceptable_keys(self): |
717 | 497 | self.requireFeature(features.gpgme) | 424 | self.requireFeature(features.gpg) |
718 | 498 | self.import_keys() | 425 | self.import_keys() |
719 | 499 | my_gpg = gpg.GPGStrategy(FakeConfig()) | 426 | my_gpg = gpg.GPGStrategy(FakeConfig()) |
720 | 500 | my_gpg.set_acceptable_keys("bazaar@example.com") | 427 | my_gpg.set_acceptable_keys("bazaar@example.com") |
721 | @@ -502,7 +429,7 @@ | |||
722 | 502 | [u'B5DEED5FCB15DAE6ECEF919587681B1EE3080E45']) | 429 | [u'B5DEED5FCB15DAE6ECEF919587681B1EE3080E45']) |
723 | 503 | 430 | ||
724 | 504 | def test_set_acceptable_keys_from_config(self): | 431 | def test_set_acceptable_keys_from_config(self): |
726 | 505 | self.requireFeature(features.gpgme) | 432 | self.requireFeature(features.gpg) |
727 | 506 | self.import_keys() | 433 | self.import_keys() |
728 | 507 | my_gpg = gpg.GPGStrategy(FakeConfig( | 434 | my_gpg = gpg.GPGStrategy(FakeConfig( |
729 | 508 | 'acceptable_keys=bazaar@example.com')) | 435 | 'acceptable_keys=bazaar@example.com')) |
730 | @@ -511,7 +438,7 @@ | |||
731 | 511 | [u'B5DEED5FCB15DAE6ECEF919587681B1EE3080E45']) | 438 | [u'B5DEED5FCB15DAE6ECEF919587681B1EE3080E45']) |
732 | 512 | 439 | ||
733 | 513 | def test_set_acceptable_keys_unknown(self): | 440 | def test_set_acceptable_keys_unknown(self): |
735 | 514 | self.requireFeature(features.gpgme) | 441 | self.requireFeature(features.gpg) |
736 | 515 | my_gpg = gpg.GPGStrategy(FakeConfig()) | 442 | my_gpg = gpg.GPGStrategy(FakeConfig()) |
737 | 516 | self.notes = [] | 443 | self.notes = [] |
738 | 517 | def note(*args): | 444 | def note(*args): |
739 | @@ -526,10 +453,10 @@ | |||
740 | 526 | class TestDisabled(TestCase): | 453 | class TestDisabled(TestCase): |
741 | 527 | 454 | ||
742 | 528 | def test_sign(self): | 455 | def test_sign(self): |
744 | 529 | self.assertRaises(errors.SigningFailed, | 456 | self.assertRaises(gpg.SigningFailed, |
745 | 530 | gpg.DisabledGPGStrategy(None).sign, 'content') | 457 | gpg.DisabledGPGStrategy(None).sign, 'content') |
746 | 531 | 458 | ||
747 | 532 | def test_verify(self): | 459 | def test_verify(self): |
749 | 533 | self.assertRaises(errors.SignatureVerificationFailed, | 460 | self.assertRaises(gpg.SignatureVerificationFailed, |
750 | 534 | gpg.DisabledGPGStrategy(None).verify, 'content', | 461 | gpg.DisabledGPGStrategy(None).verify, 'content', |
751 | 535 | 'testament') | 462 | 'testament') |
752 | 536 | 463 | ||
753 | === modified file 'breezy/tests/test_merge_directive.py' | |||
754 | --- breezy/tests/test_merge_directive.py 2017-06-10 00:17:06 +0000 | |||
755 | +++ breezy/tests/test_merge_directive.py 2017-07-04 21:10:26 +0000 | |||
756 | @@ -464,8 +464,6 @@ | |||
757 | 464 | class FakeBranch(object): | 464 | class FakeBranch(object): |
758 | 465 | def get_config_stack(self): | 465 | def get_config_stack(self): |
759 | 466 | return self | 466 | return self |
760 | 467 | def gpg_signing_command(self): | ||
761 | 468 | return 'loopback' | ||
762 | 469 | md = self.make_merge_directive('example:', 'sha', time, timezone, | 467 | md = self.make_merge_directive('example:', 'sha', time, timezone, |
763 | 470 | 'http://example.com', source_branch="http://example.org", | 468 | 'http://example.com', source_branch="http://example.org", |
764 | 471 | patch='booga', patch_type='diff') | 469 | patch='booga', patch_type='diff') |
765 | 472 | 470 | ||
766 | === modified file 'doc/en/release-notes/brz-3.0.txt' | |||
767 | --- doc/en/release-notes/brz-3.0.txt 2017-06-22 01:52:28 +0000 | |||
768 | +++ doc/en/release-notes/brz-3.0.txt 2017-07-04 21:10:26 +0000 | |||
769 | @@ -56,6 +56,13 @@ | |||
770 | 56 | This simplifies ``brz init --help``. | 56 | This simplifies ``brz init --help``. |
771 | 57 | (Neil Martinsen-Burrell, #330494) | 57 | (Neil Martinsen-Burrell, #330494) |
772 | 58 | 58 | ||
773 | 59 | * ``python-gpg`` is now used for checking GPG signatures rather than | ||
774 | 60 | ``python-gpgme``. (Jelmer Vernooij, #1702308) | ||
775 | 61 | |||
776 | 62 | * ``python-gpg`` is now used for signing commits, rather than shelling | ||
777 | 63 | out to the gnupg command. The ``gpg_signing_command`` option has been | ||
778 | 64 | removed. (Jelmer Vernooij, #847388) | ||
779 | 65 | |||
780 | 59 | New Features | 66 | New Features |
781 | 60 | ************ | 67 | ************ |
782 | 61 | 68 |