From 1c190600b66dd9b9de1f3f61111f8db51b055d81 Mon Sep 17 00:00:00 2001 From: "rmlibre@riseup.net" Date: Sun, 15 Dec 2019 21:17:05 -0500 Subject: [PATCH] v0.4.3 update - fixed __init__() bug not setting user fingerprint --- CHANGES.md | 11 ++++++-- LICENSE | 2 +- README.rst | 20 ++++++++++----- setup.py | 4 +-- tests/test_tiny_gnupg.py | 34 +++++++++++++++++++------ tiny_gnupg/__init__.py | 4 +-- tiny_gnupg/tiny_gnupg.py | 54 ++++++++++++++++++++-------------------- 7 files changed, 82 insertions(+), 47 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6a83866..4abe752 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,13 +1,20 @@ -# Changes for version 0.4.2 +# Changes for version 0.4.3 ## Known Issues - Because of Debian [bug #930665](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=930665), and related GnuPG [bug #T4393](https://dev.gnupg.org/T4393), importing keys from the default keyserver [keys.openpgp.org](https://keys.openpgp.org/) doesn't work automatically on all systems. Not without email confirmation, at least. That's because the keyserver will not publish uid information attached to a key before a user confirms access to the email address assigned to the uploaded key. And, because GnuPG folks are still holding up the merging, and back-porting, of patches that would allow GnuPG to automatically handle keys without uids gracefully. This effects the `network_import()` method specifically, but also the `text_import()` and `file_import()` methods, if they happen to be passed a key or filename argument which refers to a key without uid information. The gpg2 binary in this package can be replaced manually if a user's system has access to a patched version. - This program may only be reliably compatible with keys that are also created with this program. That's because our terminal parsing is reliant on specific metadata to be similar across all encountered keys. It seems most keys have successfully been parsed with recent updates, though more testing is needed. - Currently, the package is part synchronous, and part asynchronous. This is not ideal, so a decision has to be made: either to stay mixed style, or choose one consistent style. - We're still in unstable and have to build out our test suite. Contributions welcome. ## Minor Changes +- Changed package description to name more specifically the kind of ECC keys this package handles. +## Major Changes +- Fixed bug in `__init__()` caused by the set_base_command() not being called before the base commands are used. This leading to the fingerprint for a persistent user not being set automatically. + + +# Changes for version 0.4.2 +## Minor Changes - Added some keyword argument names to README.rst tutorials. - Added section in README.rst about torification. -# Major Changes +## Major Changes - Added a check in `encrypt()` for the recipient key in the local keyring which throws if it doesn't exist. This is to prevent gnupg from using wkd to contact the network to find the key on a keyserver. - Added a new `torify=False` kwarg to `__init__()` which prepends `"torify"` to each gpg2 command if set to `True`. This will make sure that if gnupg makes any silent connections to keyservers or the web, that they are run through tor and don't expose a users ip address inadvertently. diff --git a/LICENSE b/LICENSE index e056a45..11c011d 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ This file is part of tiny_gnupg, a small-as-possible solution for -handling GnuPG ed-25519 ECC keys. +handling GnuPG ed25519 ECC keys. Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html Copyright © 2019-2020 Gonzo Investigatory Journalism Agency, LLC diff --git a/README.rst b/README.rst index 5460ee7..be0a488 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ -tiny_gnupg - A small-as-possible solution for handling GnuPG ECC keys. -====================================================================== +tiny_gnupg - A small-as-possible solution for handling GnuPG ed25519 ECC keys. +=============================================================================== A small, simple & intuitive wrapper for creating, using and managing GnuPG's Ed-25519 curve keys. We are in favor of reducing code size and complexity with strong and bias defaults over flexibility in the api. @@ -162,11 +162,9 @@ Networking Example # upload, and import keys. This provides a nice, default layer of # privacy to our communication needs. Have fun little niblets! +These networking tools work off instances of aiohttp.ClientSession. To learn more about how to use their POST and GET requests, you can read the docs here_. - # These networking tools work off instances of aiohttp.ClientSession. - # To learn more about how to use their POST and GET requests, you - # can read the docs here: - # https://docs.aiohttp.org/en/stable/client_advanced.html#client-session +.. _here: https://docs.aiohttp.org/en/stable/client_advanced.html#client-session About Torification @@ -234,6 +232,16 @@ Extras # And secret keys, but really, keep those safe! -> run(gpg.file_export(path=path_to_file, uid=gpg.email, secret=True)) + # The keys don't have to be exported to a file. Instead they can + # be exported as strings -> + my_key = gpg.text_export(uid=gpg.fingerprint) + + # So can secret keys (Be careful!) -> + my_secret_key = gpg.text_export(gpg.fingerprint, secret=True) + + # And they can just as easily be imported from strings -> + gpg.text_import(key=my_key) + # When a user is done with a key, it can be deleted from the package # keyring like this -> diff --git a/setup.py b/setup.py index 14a315d..d4fa672 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ # This file is part of tiny_gnupg, a small-as-possible solution for -# handling GnuPG ed-25519 ECC keys. +# handling GnuPG ed25519 ECC keys. # # Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html # Copyright © 2019-2020 Gonzo Investigatory Journalism Agency, LLC @@ -11,7 +11,7 @@ from setuptools import setup, find_packages description = """ -tiny_gnupg - A small-as-possible solution for handling GnuPG ed-25519 ECC keys. +tiny_gnupg - A small-as-possible solution for handling GnuPG ed25519 ECC keys. """.replace("\n", "") with open("README.rst", "r") as readme: diff --git a/tests/test_tiny_gnupg.py b/tests/test_tiny_gnupg.py index a5408aa..70ff170 100644 --- a/tests/test_tiny_gnupg.py +++ b/tests/test_tiny_gnupg.py @@ -1,5 +1,5 @@ # This file is part of tiny_gnupg, a small-as-possible solution for -# handling GnuPG ed-25519 ECC keys. +# handling GnuPG ed25519 ECC keys. # # Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html # Copyright © 2019-2020 Gonzo Investigatory Journalism Agency, LLC @@ -41,9 +41,29 @@ def gpg(): def test_instance(gpg): gpg.gen_key() + test_gpg = GnuPG(gpg.username, gpg.email, gpg.passphrase) + assert gpg.username == test_gpg.username + assert gpg.email == test_gpg.email + assert gpg.passphrase == test_gpg.passphrase + assert gpg.port == test_gpg.port + assert gpg.tor_port == test_gpg.tor_port + assert gpg.home == test_gpg.home + assert gpg.executable == test_gpg.executable + assert gpg._connector == test_gpg._connector + assert gpg._session == test_gpg._session + assert gpg._search_string == test_gpg._search_string + assert gpg.keyserver == test_gpg.keyserver + assert str(gpg.port) in gpg.keyserver + assert gpg.keyserver_export_api == test_gpg.keyserver_export_api + assert gpg.keyserver_verify_api == test_gpg.keyserver_verify_api + assert gpg.searchserver == test_gpg.searchserver + assert gpg.base_command == test_gpg.base_command + assert gpg.base_passphrase_command == test_gpg.base_passphrase_command + # assert gpg.username == "testing_user" assert gpg.email == "testing_user@testing.testing" - assert gpg.fingerprint == "" or type(gpg.fingerprint) == str + assert len(gpg.fingerprint) == 40 + assert type(gpg.fingerprint) == str assert gpg.passphrase == "test_passphrase" assert gpg.home.endswith("gpghome") assert gpg.executable.endswith("gpg2") @@ -106,10 +126,10 @@ def test_cipher(gpg): uid=gpg.fingerprint, sign=False, ) - assert gpg.decrypt(encrypted_message_0) == message + "\n" - assert gpg.decrypt(encrypted_message_1) == message + "\n" - assert gpg.decrypt(encrypted_message_2) == message + "\n" - assert gpg.decrypt(encrypted_message_3) == message + "\n" + assert gpg.decrypt(encrypted_message_0) == message + assert gpg.decrypt(encrypted_message_1) == message + assert gpg.decrypt(encrypted_message_2) == message + assert gpg.decrypt(encrypted_message_3) == message signed_message_0 = gpg.sign(message) signed_message_1 = gpg.sign(signed_message_0) signed_message_2 = gpg.sign(signed_message_1) @@ -192,7 +212,7 @@ async def gather_looper(gpg, uid): async def looper(gpg, uid): tasks = [] - for i in range(5): + for i in range(4): tasks.append(new_task(gpg.search(uid))) return tasks diff --git a/tiny_gnupg/__init__.py b/tiny_gnupg/__init__.py index 02e6d59..89f05a7 100644 --- a/tiny_gnupg/__init__.py +++ b/tiny_gnupg/__init__.py @@ -1,5 +1,5 @@ # This file is part of tiny_gnupg, a small-as-possible solution for -# handling GnuPG ECC keys. +# handling GnuPG ed25519 ECC keys. # # Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html # Copyright © 2019-2020 Gonzo Investigatory Journalism Agency, LLC @@ -8,6 +8,6 @@ # All rights reserved. # -__version__ = "0.4.2" +__version__ = "0.4.3" from .tiny_gnupg import GnuPG, __all__ diff --git a/tiny_gnupg/tiny_gnupg.py b/tiny_gnupg/tiny_gnupg.py index 9637190..5586885 100644 --- a/tiny_gnupg/tiny_gnupg.py +++ b/tiny_gnupg/tiny_gnupg.py @@ -1,5 +1,5 @@ # This file is part of tiny_gnupg, a small-as-possible solution for -# handling GnuPG ECC keys. +# handling GnuPG ed25519 ECC keys. # # Licensed under the GPLv3: http://www.gnu.org/licenses/gpl-3.0.html # Copyright © 2019-2020 Gonzo Investigatory Journalism Agency, LLC @@ -31,9 +31,9 @@ def __init__( self.email = email self.username = username self.passphrase = passphrase + self.set_base_command(torify) # set before calling command() self.set_fingerprint(email) self.set_network_variables() - self.set_base_command(torify) def set_homedir(self, path=HOME_PATH): self.home = self.format_homedir(path) @@ -51,6 +51,29 @@ def set_home_permissions(self, home): except: print(f"Invalid permission to modify home folder: {home}") + def set_base_command(self, torify=False): + torify = ["torify"] if torify else [] + self.base_passphrase_command = torify + [ + self.executable, + "--yes", + "--batch", + "--quiet", + "--homedir", + self.home, + "--pinentry-mode", + "loopback", + "--passphrase-fd", + "0", + ] + self.base_command = torify + [ + self.executable, + "--yes", + "--batch", + "--quiet", + "--homedir", + self.home, + ] + def set_fingerprint(self, uid=""): try: self.fingerprint = self.key_fingerprint(uid) @@ -124,29 +147,6 @@ async def post(self, url="", **kw): async with self.network_post(url, **kw) as response: return await response.text() - def set_base_command(self, torify=False): - torify = ["torify"] if torify else [] - self.base_passphrase_command = torify + [ - self.executable, - "--yes", - "--batch", - "--quiet", - "--homedir", - self.home, - "--pinentry-mode", - "loopback", - "--passphrase-fd", - "0", - ] - self.base_command = torify + [ - self.executable, - "--yes", - "--batch", - "--quiet", - "--homedir", - self.home, - ] - def command(self, *options, with_passphrase=False): if with_passphrase: return self.base_passphrase_command + [*options] @@ -270,7 +270,7 @@ def encrypt(self, message="", uid="", sign=True, local_user=""): inputs = self.encode_inputs(self.passphrase, "y", message) else: inputs = self.encode_inputs(self.passphrase, message) - return self.read_output(command, inputs) + return self.read_output(command, inputs[:-1]) def decrypt(self, message=""): command = self.command("-d", with_passphrase=True) @@ -294,7 +294,7 @@ def sign(self, target="", local_user="", *, key=False): "-as", with_passphrase=True, ) - inputs = self.encode_inputs(self.passphrase, target) + inputs = self.encode_inputs(self.passphrase, target)[:-1] else: raise ValueError(f"key != boolean, {type(key)} given.") return self.read_output(command, inputs)