Compare commits

...

6 Commits

Author SHA1 Message Date
Daniel Kahn Gillmor
ac8d7238db python: overhaul logic of Context.decrypt()
* lang/python/src/core.py (Context.decrypt): simplify and clarify the
logic behind handling verify=False.
* lang/python/tests/t-decrypt.py: ensure that we test verify=False

--

The function-internal variables were pretty unclear to the reader, and
the logic caused pretty nasty breakage when verify=False.

GnuPG-Bug-Id: 4271
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2018-12-05 02:41:16 +03:00
Daniel Kahn Gillmor
5e21e61cfe python: ctx.decrypt() has problematic error handling
* lang/python/src/core.py (Context.decrypt): document odd
error-handling behavior as a potential problem to be addressed.

Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2018-12-05 02:40:25 +03:00
Daniel Kahn Gillmor
fefa46173e python: Clarify the meaning of ctx.decrypt(verify=[])
* lang/python/src/core.py (Context.decrypt): docstring clarification
of what it means to pass an empty list to the verify argument.

Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2018-12-05 02:40:24 +03:00
Daniel Kahn Gillmor
30ddb2cabc python: gpg.Context.decrypt verify_sigs and sink_result are bools
Both of these function-internal variables are never used for anything
other than a binary state.  Implement them as the booleans they are.
Otherwise, casual readers of the code might think that they're
supposed to represent something other than a flag (e.g. "verify_sigs"
could mean "the signatures to verify", and "sink_result" could mean
"the place where we sink the result").

Signed-Off-By: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2018-12-05 02:40:24 +03:00
Daniel Kahn Gillmor
9a1903cc42 python: clarify documentation for verify argument for Context.decrypt()
It's easy to miss that verify can take a list of keys.  Make it more
obvious to the average python dev who reads docstrings.

Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2018-12-05 02:40:24 +03:00
Daniel Kahn Gillmor
827a2f3ad5 python: simplify Context.decrypt()
In the course of trying to address https://dev.gnupg.org/T4271, i
discovered that gpg.Context.decrypt() has a bit of superfluous code.
This changeset is intended to simplify the code without making any
functional changes.

Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2018-12-05 02:40:24 +03:00
2 changed files with 44 additions and 59 deletions

View File

@ -342,7 +342,10 @@ class Context(GpgmeWrapper):
Decrypt the given ciphertext and verify any signatures. If Decrypt the given ciphertext and verify any signatures. If
VERIFY is an iterable of keys, the ciphertext must be signed VERIFY is an iterable of keys, the ciphertext must be signed
by all those keys, otherwise an error is raised. by all those keys, otherwise an error is raised. Note: if
VERIFY is an empty iterable, that is treated the same as
passing verify=True (that is, do verify signatures, but no
specific keys are required).
If the ciphertext is symmetrically encrypted using a If the ciphertext is symmetrically encrypted using a
passphrase, that passphrase can be given as parameter, using a passphrase, that passphrase can be given as parameter, using a
@ -352,7 +355,8 @@ class Context(GpgmeWrapper):
Keyword arguments: Keyword arguments:
sink -- write result to sink instead of returning it sink -- write result to sink instead of returning it
passphrase -- for symmetric decryption passphrase -- for symmetric decryption
verify -- check signatures (default True) verify -- check signatures (boolean or iterable of keys,
see above) (default True)
Returns: Returns:
plaintext -- the decrypted data (or None if sink is given) plaintext -- the decrypted data (or None if sink is given)
@ -366,8 +370,8 @@ class Context(GpgmeWrapper):
GPGMEError -- as signaled by the underlying library GPGMEError -- as signaled by the underlying library
""" """
sink_result = None do_sig_verification = False
verify_sigs = None required_keys = None
plaintext = sink if sink else Data() plaintext = sink if sink else Data()
if passphrase is not None: if passphrase is not None:
@ -381,26 +385,25 @@ class Context(GpgmeWrapper):
self.set_passphrase_cb(passphrase_cb) self.set_passphrase_cb(passphrase_cb)
try: try:
if verify is not None: if isinstance(verify, bool):
if isinstance(verify, bool) is True: do_sig_verification = verify
if verify is False: elif verify is None:
verify = True warnings.warn(
sink_result = True "ctx.decrypt called with verify=None, should be bool or iterable (treating as False).",
category=DeprecationWarning)
do_sig_verification = False
else: else:
pass # we hope this is an iterable:
elif isinstance(verify, list) is True: required_keys = verify
if len(verify) > 0: do_sig_verification = True
verify_sigs = True
else: if do_sig_verification:
pass
else:
verify = True
self.op_decrypt_verify(ciphertext, plaintext) self.op_decrypt_verify(ciphertext, plaintext)
else: else:
self.op_decrypt(ciphertext, plaintext) self.op_decrypt(ciphertext, plaintext)
except errors.GPGMEError as e: except errors.GPGMEError as e:
result = self.op_decrypt_result() result = self.op_decrypt_result()
if verify is not None and sink_result is None: if do_sig_verification:
verify_result = self.op_verify_result() verify_result = self.op_verify_result()
else: else:
verify_result = None verify_result = None
@ -415,7 +418,7 @@ class Context(GpgmeWrapper):
result = self.op_decrypt_result() result = self.op_decrypt_result()
if verify is not None and sink_result is None: if do_sig_verification:
verify_result = self.op_verify_result() verify_result = self.op_verify_result()
else: else:
verify_result = None verify_result = None
@ -426,14 +429,17 @@ class Context(GpgmeWrapper):
raise errors.UnsupportedAlgorithm(result.unsupported_algorithm, raise errors.UnsupportedAlgorithm(result.unsupported_algorithm,
results=results) results=results)
if verify: if do_sig_verification:
# FIXME: should we really throw BadSignature, even if
# we've encountered some good signatures? as above, once
# we hit this error, there is no way to accept it and
# continue to process the remaining signatures.
if any(s.status != errors.NO_ERROR if any(s.status != errors.NO_ERROR
for s in verify_result.signatures): for s in verify_result.signatures):
raise errors.BadSignatures(verify_result, results=results) raise errors.BadSignatures(verify_result, results=results)
if required_keys is not None:
if verify_sigs is not None:
missing = [] missing = []
for key in verify: for key in required_keys:
ok = False ok = False
for subkey in key.subkeys: for subkey in key.subkeys:
for sig in verify_result.signatures: for sig in verify_result.signatures:
@ -447,29 +453,8 @@ class Context(GpgmeWrapper):
if not ok: if not ok:
missing.append(key) missing.append(key)
if missing: if missing:
try:
raise errors.MissingSignatures(verify_result, missing, raise errors.MissingSignatures(verify_result, missing,
results=results) results=results)
except errors.MissingSignatures as e:
raise e
# mse = e
# mserr = "gpg.errors.MissingSignatures:"
# print(mserr, miss_e, "\n")
# # The full details can then be found in mse.results,
# # mse.result, mse.missing if necessary.
# mse_list = []
# msp = "Missing signatures from: \n".format()
# print(msp)
# for key in mse.missing:
# mse_list.append(key.fpr)
# msl = []
# msl.append(key.fpr)
# for user in key.uids:
# msl.append(user.name)
# msl.append(user.email)
# # msl.append(user.uid)
# print(" ".join(msl))
# raise mse
return results return results

View File

@ -38,7 +38,7 @@ support.print_data(sink)
# Idiomatic interface. # Idiomatic interface.
with gpg.Context() as c: with gpg.Context() as c:
plaintext, _, _ = c.decrypt(open(support.make_filename("cipher-1.asc"))) plaintext, _, _ = c.decrypt(open(support.make_filename("cipher-1.asc")), verify=False)
assert len(plaintext) > 0 assert len(plaintext) > 0
assert plaintext.find(b'Wenn Sie dies lesen k') >= 0, \ assert plaintext.find(b'Wenn Sie dies lesen k') >= 0, \
'Plaintext not found' 'Plaintext not found'