Skip to content

Commit

Permalink
v0.5.7 update - instance-isolated identities
Browse files Browse the repository at this point in the history
  • Loading branch information
rmlibre committed Dec 25, 2019
1 parent f3ffdff commit 34b7b90
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 12 deletions.
26 changes: 26 additions & 0 deletions tiny_gnupg.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
Expand Up @@ -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
=========================

Expand Down
2 changes: 1 addition & 1 deletion tiny_gnupg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@
# All rights reserved.
#

__version__ = "0.5.6"
__version__ = "0.5.7"

from .tiny_gnupg import GnuPG, run, __all__
45 changes: 34 additions & 11 deletions tiny_gnupg/tiny_gnupg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
"""
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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:
Expand All @@ -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=""):
Expand All @@ -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",
Expand All @@ -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:
Expand All @@ -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
Expand Down

0 comments on commit 34b7b90

Please sign in to comment.