From 34b7b908081d0c00c37ff8fcd33494454a3db654 Mon Sep 17 00:00:00 2001 From: "rmlibre@riseup.net" Date: Tue, 24 Dec 2019 20:56:22 -0500 Subject: [PATCH] v0.5.7 update - instance-isolated identities --- tiny_gnupg.egg-info/PKG-INFO | 26 +++++++++++++++++++++ tiny_gnupg/__init__.py | 2 +- tiny_gnupg/tiny_gnupg.py | 45 +++++++++++++++++++++++++++--------- 3 files changed, 61 insertions(+), 12 deletions(-) diff --git a/tiny_gnupg.egg-info/PKG-INFO b/tiny_gnupg.egg-info/PKG-INFO index f70c9b1..2fd07ca 100644 --- a/tiny_gnupg.egg-info/PKG-INFO +++ b/tiny_gnupg.egg-info/PKG-INFO @@ -392,6 +392,32 @@ Description: tiny_gnupg - A small-as-possible solution for handling GnuPG ed2551 ============= + Changes for version 0.5.7 + ========================= + + Minor Changes + ------------- + + - Tests added to include checks for instance-isolated identities. + + + Major Changes + ------------- + + - ``reset_daemon()`` calls added to ``decrypt()``, ``verify()``, ``sign()`` + & ``encrypt()``. This call kills the gpg-agent process & restarts it, + which in turn wipes the caching of secret keys available on the system + without a passphrase. This is crucial for users of applications with + multiple GnuPG objects that handle separate key identities. That's + because these methods will now throw ``PermissionError`` or ``LookupError`` + if a private key operation is needed from an instance which is already + assigned to another private key in the keyring. This gives some important + anonymity protections to users. + - More improvements to error reporting. + + + + Changes for version 0.5.6 ========================= diff --git a/tiny_gnupg/__init__.py b/tiny_gnupg/__init__.py index 94578ca..3551c62 100644 --- a/tiny_gnupg/__init__.py +++ b/tiny_gnupg/__init__.py @@ -8,6 +8,6 @@ # All rights reserved. # -__version__ = "0.5.6" +__version__ = "0.5.7" from .tiny_gnupg import GnuPG, run, __all__ diff --git a/tiny_gnupg/tiny_gnupg.py b/tiny_gnupg/tiny_gnupg.py index 6d97d48..56ab996 100644 --- a/tiny_gnupg/tiny_gnupg.py +++ b/tiny_gnupg/tiny_gnupg.py @@ -186,9 +186,28 @@ def encode_inputs(self, *inputs): def read_output(self, command=(), inputs=b"", **kw): """Quotes terminal escape characters & runs user commands""" - return check_output( - [quote(part) for part in command], input=inputs, **kw - ).decode() + try: + return check_output( + [quote(part) for part in command], input=inputs, **kw + ).decode() + except Exception as source: + error = source + try: + kw.pop("stderr") if "stderr" in kw else 0 + check_output( + [quote(part) for part in command], + input=inputs, + stderr=STDOUT, + **kw, + ).decode() + except CalledProcessError as permissions_check: + notice = "Passphrase wrong, inexistent key, or invalid rights " + notice += "to access secret key." + warning = PermissionError(notice) + warning.inputs = inputs + warning.command = command + warning.output = permissions_check.output.decode() + raise warning if "Bad passphrase" in warning.output else error def gen_key(self): """ @@ -369,6 +388,7 @@ def encrypt(self, message="", uid="", sign=True, local_user=""): matching ``local_user`` or defaults to instance key. Optionally, if ``sign`` == False, ``message`` won't be signed. """ + self.reset_daemon() if sign else 0 uid = self.key_fingerprint(uid) # avoid wkd lookups command = self.command( "--command-fd", @@ -401,6 +421,7 @@ async def auto_encrypt( def decrypt(self, message=""): """Decrypts ``message`` autodetecting correct key from keyring""" + self.reset_daemon() fingerprint = self.packet_fingerprint(message) fingerprint = self.key_fingerprint(fingerprint) try: @@ -412,13 +433,13 @@ def decrypt(self, message=""): try: self.read_output(command, inputs, stderr=STDOUT) except CalledProcessError as error: - output = error.output - error_lines = output.decode().strip().split("\n") + error_lines = error.output.decode().strip().split("\n") sentinel = "gpg: using" uid = [line[-40:] for line in error_lines if sentinel in line] - notice = f"UID '{uid}' not in package keyring" + uid = uid[-1] if uid else "" + notice = f"UID '{uid}' not in the instance's keyring." warning = LookupError(notice) - warning.value = uid[-1] if uid else fingerprint + warning.value = uid if uid else fingerprint raise warning async def auto_decrypt(self, message=""): @@ -438,6 +459,7 @@ def sign(self, target="", local_user="", *, key=False): uid or the instance default. Optionally signs ``target`` message if ``key`` == False. """ + self.reset_daemon() if key == True: # avoid truthiness command = self.command( "--local-user", @@ -464,6 +486,7 @@ def verify(self, message=""): Verifies signed ``message`` if the corresponding public key is in the local keyring. """ + self.reset_daemon() fingerprint = self.packet_fingerprint(message) fingerprint = self.key_fingerprint(fingerprint) try: @@ -489,15 +512,15 @@ async def auto_verify(self, message=""): def raw_list_keys(self, uid="", secret=False): """Returns the terminal output of the --list-keys ``uid`` option""" - secret = "-secret" if secret else "" + secret = "secret-" if secret else "" if uid: - command = self.command(f"--list{secret}-keys", uid) + command = self.command(f"--list-{secret}keys", uid) else: - command = self.command(f"--list{secret}-keys") + command = self.command(f"--list-{secret}keys") try: return self.read_output(command) except CalledProcessError: - notice = f"UID '{uid}' not in package keyring" + notice = f"UID '{uid}' not in package {secret}keyring" warning = LookupError(notice) warning.value = uid raise warning