Package: tiny-gnupg
tiny_gnupg - A small-as-possible solution for handling GnuPG ed25519 ECC keys.A linux specific, small, simple & intuitive wrapper for creating, using
and managing GnuPG’s Ed25519 curve keys. In our design, we favor
reducing code size & complexity with strong, bias defaults over
flexibility in the api. Our goal is to turn the powerful, complex,
legacy gnupg system into a fun and safe tool to develop with.This project is currently in unstable beta. It works like a charm, but
there’s likely, and often bugs floating around, and the api is subject
to change. Contributions are welcome.Table Of ContentsInstallBasic CommandsNetworking ExamplesAbout TorificationMore CommandsRetiring KeysInstallsudoapt-getinstalltortorsocksgnupg2gpg-agentpipinstall--user--upgradetiny_gnupgBasic CommandsTheGnuPGclass’s instances are the primary interface for running commands & managing keys using the gpg2 executable.fromtiny_gnupgimportGnuPG,runPATH_TO_GPG_BINARY="/usr/bin/gpg2"gpg=GnuPG(email_address="bob@user.net",passphrase="bobs's passphrase",executable=PATH_TO_GPG_BINARY,)# This will generate a primary ed25519 ECC certifying key, and three# subkeys, one each for the authentication, encryption, and signing# functionalities.gpg.generate_key()# Now this fingerprint can be used with arbitrary gpg2 commands.gpg.fingerprint# But the key is stored in the package's local keyring. To# talk to the package's gpg environment, an arbitrary command# can be constructed like this ->options=["--armor","--encrypt","-r",gpg.fingerprint]command=gpg.encode_command(*options)inputs=gpg.encode_inputs("Message to myself")encrypted_message=gpg.read_output(command,inputs)# If a command would invoke the need for a passphrase, the# with_passphrase kwarg should be set to True ->command=gpg.encode_command(*options,with_passphrase=True)# The passphrase then needs to be the first arg passed to# encode_inputs ->inputs=gpg.encode_inputs(gpg.user.passphrase,*other_inputs)# The list of keys in the package's environment can be accessed# from the list_keys() method, which returns a dict ->gpg.list_keys()>>>{fingerprint:email_address,...}# Or retrieve a specific key where a searchable portion of its uid# information is known, like an email address or fingerprint ->gpg.list_keys("bob@user.net")>>>{"EE36F0584971280730D76CEC94A470B77ABA6E81":"bob@user.net"}# Let's try encrypting a message to Alice, whose public key is# stored on keys.openpgp.org/# First, we'll import Alice's key from the keyserver (This requires# a Tor system installation. Or an open TorBrowser, and the tor_port# attribute set to 9150) -># Optional: gpg.keyserver.network.tor_port = 9150run(gpg.network_import(uid="alice@email.domain"))# Then encrypt a message with Alice's key and sign it ->msg="So, what's the plan this Sunday, Alice?"encrypted_message=gpg.encrypt(message=msg,uid="alice@email.domain",sign=True)# The process of encrypting a message to a peer whose public key# might not be in the local package keyring is conveniently available# in a single method. It automatically searches for the recipient's# key on the keyserver so it can be used to encrypt the message ->run(gpg.auto_encrypt(msg,"alice@email.domain"))# Signing is automatic# We could directly send a copy of our key to Alice, or upload it to# the keyserver. Alice will need a copy so the signature on the# message can be verified. So let's upload it to the keyserver ->run(gpg.network_export(uid="bob@user.net"))# Alice could now import our key (after we do an email verification# with the keyserver) ->run(gpg.network_import("bob@user.net"))# Then Alice can simply receive the encrypted message and decrypt it ->decrypted_msg=gpg.decrypt(encrypted_message)# The process of decrypting a encrypted & signed message from a peer# whose public key might not be in the local package keyring is# conveniently available in a single method. It automatically determines# the signing key fingerprint, and searches for it on the keyserver# to verify the signature ->decrypted_msg=run(gpg.auto_decrypt(encrypted_message))On most systems, because of a bug inGnuPG, email verification of uploaded keys will be necessary for others to import them from the keyserver. That’s because GnuPG will throw an error immediately upon trying to import keys with their uid information stripped off.The package no longer comes with its own gpg2 binary. Your system gpg2 executable is probably located at: /usr/bin/gpg2. You could also type:whereis gpg2to find it. If it’s not installed, you’ll have to install it with your system’s equivalent of:sudoapt-getinstall gnupg2Networking Examples# Since we use SOCKSv5 over Tor for all of our networking, as well# as the user-friendly aiohttp + aiohttp_socks libraries, the Tor# networking interface is also available to users. These utilities# allow arbitrary POST and GET requests to clearnet, or onionland,# websites ->fromtiny_gnupgimportGnuPG,Network,runclient=Network(tor_port=9050)asyncdefread_url(client,url):"""
    Use the instance's interface to read the page located at the url
    with a wrapper around an `aiohttp.ClientSession` context manager.
    """asyncwithclient.context_get(url)asresponse:returnawaitresponse.text()# Now we can read webpages with GET requests ->page_html=run(read_url(client,"https://keys.openpgp.org/"))# Let's try onionland ->url="http://zkaan2xfbuxia2wpf7ofnkbz6r5zdbbvxbunvp5g2iebopbfc4iqmbad.onion/"onion_page_html=run(read_url(client,url))# Check your ip address for fun ->ip_addr=run(read_url(client,"https://icanhazip.com/"))# There's a convenience function built into the class that# basically mimics read_url() ->ip_addr=run(client.get("https://icanhazip.com/"))# POST requests can also be sent with the context_post() method.# Let's use a POST request to send the keyserver a new key we# create ->asyncdefpost_data(client,url,payload=""):"""
    Use the instance's interface to post the api payload to the
    keyserver with a wrapper around an `aiohttp.ClientSession`
    context manager.
    """asyncwithclient.context_post(url,json=payload)asresponse:returnawaitresponse.text()gpg=GnuPG(email_address="bob@user.net",passphrase="bobs's passphrase")gpg.generate_key()url=gpg.keyserver._key_export_api_urlpayload={"keytext":gpg.text_export(uid=gpg.fingerprint)}api_token_json=run(post_data(client,url,payload))# There's also a convenience function built into the class that# mimics post_data() ->api_token_json=run(client.post(url,json=payload))# Of course, this is just for demonstration. The method that should# be used for uploading a key to the keyserver is network_export ->run(gpg.network_export(gpg.fingerprint))# And there we have it, it's super simple. And these requests have# the added benefit of being completely routed through Tor. The# keyserver here also has a v3 onion address which we use to query,# upload, and import keys. This provides a nice, default layer of# privacy to our communication needs.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 docshere.About TorificationA user can make sure that any connections the gnupg binary makes with the network are always run through Tor by settingtorify=Trueduring initialization.fromtiny_gnupgimportGnuPGgpg=GnuPG(**user_details,torify=True)This is helpful because there are gnupg settings which cause certain commands to do automatic connections to the web. For instance, when encrypting, gnupg may be set to automatically search for the recipient’s key on a keyserver if it’s not in the local keyring. This doesn’t normally effecttiny_gnupgbecause it doesn’t use gnupg’s networking interface. It ensures Tor connections through theaiohttp_sockslibrary. But, if gnupg does make these kinds of connections silently, using torify can prevent a user’s IP address from being inadvertently revealed.Using torify requires a Tor installation on the user system. If the user is running Debian / Ubuntu, then thisguidecould be helpful.More Commands# An instance can also be constructed from lower-level objects ->fromtiny_gnupgimportBaseGnuPG,User,GnuPGConfig,runPATH_TO_GPG_BINARY="/usr/bin/gpg2"# Passphrases can contain any characters, even emojis ->user=User(email_address="bob@user.net",passphrase="✅🐎🔋📌")config=GnuPGConfig(executable=PATH_TO_GPG_BINARY,torify=True)gpg=BaseGnuPG(user,config=config)# It turns out that the encrypt() method automatically signs the# message being encrypted. So, the `sign=False` flag only has to be# passed when a user doesn't want to sign a message ->encrypted_unsigned_message=gpg.encrypt(message="sending message as an unidentified sender",uid="alice@email.domain",# sending to alice,sign=False,# no sender identification)# It also turns out, a user can sign things independently from# encrypting ->message_to_verify="maybe a hash of a file?"signed_data=gpg.sign(target=message_to_verify)assertmessage_to_verify==gpg.decrypt(signed_data)# And verify a signature without checking the signed value ->gpg.verify(message=signed_data)# throws if invalid# Or sign a key in the package's keyring ->gpg.sign("alice@email.domain",key=True)# Importing key files is also a thing ->path_to_file="/home/user/keyfiles/"gpg.file_import(path=path_to_file+"alices_key.asc")# As well as exporting public keys ->gpg.file_export(path=path_to_file,uid=gpg.user.email_address)# And secret keys, but really, keep those safe! ->gpg.file_export(path=path_to_file,uid=gpg.user.email_address,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)Retiring KeysAfter a user no longer considers a key useful, or wants to dissociate from the key, then they have some options:fromtiny_gnupgimportGnuPG,runPATH_TO_GPG_BINARY="/usr/bin/gpg2"gpg=GnuPG(email_address="bob@user.net",passphrase="bobs's passphrase",executable=PATH_TO_GPG_BINARY,)# They can revoke their key then distribute it publicly (somehow)# (the keyserver can't currently handle key revocations) ->revoked_key=gpg.revoke(gpg.fingerprint)# <--  Distribute this!# Uploading the revoked key will only strip the user ID information# from the key on the keyserver. It won't explicitly let others know# the key has been retired. However, this action cannot be undone ->run(gpg.network_export(gpg.fingerprint))# The key can also be deleted from the package keyring like this ->gpg.delete(uid="bob@user.net")Known IssuesBecause of Debianbug #930665, & related GnuPGbug #T4393,
importing keys from the default keyserverkeys.openpgp.orgdoesn’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, & back-porting, of patches that would allow
GnuPG to automatically handle keys without uids gracefully. This
effects thenetwork_import()method specifically, but also thetext_import()&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.Because of GnuPGbug #T3065, & relatedbug #1788190, the--keyserver&--keyserver-optionshttp-proxyoptions won’t
work with onion addresses, & they cause a crash if a keyserver
lookup is attempted. This is not entirely an issue for us since we
don’t use gnupg’s networking interface. In fact, we set these
environment variables anyway to crash on purpose if gnupg tries to
make a network connection. And in case the bug ever gets fixed (it
won’t), or by accident the options do work in the future, then a tor
SOCKSv5 connection will be used instead of a raw connection.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.The tests don’t currently work when a tester’s system has a system
installation of tiny_gnupg, & the tests are being run from a local
git repo directory. That’s because the tests import tiny_gnupg, but
if the program is installed in the system, then python will get
confused about which keyring to use during the tests. This will lead
to crashes & failed tests. Git clone testers probably have to run
the test script closer to their system installation, one directory up
& into a tests folder. Or pip uninstall tiny_gnupg. OR, send a pull
request with an import fix.Currently, the package is part synchronous, & 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 beta & have to build out our test suite.
Contributions welcome.The tests seems to fail on some systems because of a torsocks
filter [1][2] which blocks some syscalls. This may be patched or not
applicable on non-linux operating systems.ChangelogChanges for version 0.9.0Major ChangesThepassphrasekeyword argument is now processed through thehashlib.scryptfunction before being stored within aUserinstance
& used within theGnuPG&BaseGnuPGclasses. TheGnuPG&BaseGnuPGclasses also accept an optionalsaltkeyword-only argument. These
changes secure user keys & passwords by default with a memory-hard
key derivation function & the uniqueness of the user-specified random
salt. These changes provide better security & aren’t backwards
compatible.Theemailkeyword argument to theUser,BaseGnuPG&GnuPGclasses was changed toemail_address. The attributes in theUserclass have also mirrored this change. As well, thekey_emailmethod
on theGnuPG&BaseGnuPGclasses is nowkey_email_address.TheUserclass now does type & value checking on theusername,email_address&passphrasestrings passed into the__init__,
as well as whenever their associated property attributes are set.Minor ChangesDocumentation improvements.Various refactorings & code cleanups.More type hinting was added & improved upon.Removed the improper usage of theNoneTypefor type hinting.New constants were added to thetiny_gnupg.pymodule to specify
problematic control & whitespace characters that shouldn’t be used in
various user-defined inputs & credentials.Thefile_exportmethods of theGnuPG&BaseGnuPGclasses now
saves key files with either"public-key_"or"secret-key_"strings
prepended to them to better specify for users the context of files
saved to their filesystems.Removed the svg image file which didn’t accurately report the line
coverage with the new changes to the package.Changes for version 0.8.2Minor ChangesDocumentation improvements.Theusernamekeyword-only argument to theUser&GnuPGclasses
was given a default empty string. This change allows theusernameto be optional & ignorable by the user. When generating a key with an
instance which doesn’t have ausernamespecified, then the associated
key will also not contain a username field.Changes for version 0.8.1Minor ChangesDocumentation improvements & typo fixes.Changes for version 0.8.0Major ChangesThe newGnuPGConfig&Keyserverclasses were extracted from
theGnuPGclass.GnuPGConfigholds onto each instance’s path
strings to the system resources (like the gpg2 binary, the .conf
file, & the home directory), as well as other static constants &
instance specific settings (like the torify boolean flag). And, theKeyserverclass separates the Tor networking & key upload,
download, & searching logic.TheGnuPGclass was given a super class,BaseGnuPG, which is
initialized usingUser&GnuPGConfigobjects instead of the
strings & booleans which have until now been used to initialize aGnuPGinstance.  This allows users to choose between initializing
instances using the package’s higher-level types or python built-in
types.Thegen_keymethod ofGnuPG&BaseGnuPGwas changed togenerate_key.Minor ChangesDocstring, documentation & type annotation fixes.Improved the clarity of error messages & the UX of error handling.Improved various GnuPG terminal output parsing logics.Heavy factorings to improve clarity & better organize the codebase.Changes for version 0.7.9Minor ChangesDocstring & type annotation fixes.Small internal refactorings.Changes for version 0.7.8Major ChangesSecurity Alert: Users’ separateGnuPGinstance’s with the same
home directory, which represent distinct & different secret keys,
can only be considered to represent separate identities during
runtime if thepassphrasefor each instance is distinct &
different. Past updates of the package have mentioned separate
identities as if one instance won’t be able to access another’s
secret keys, and this isnottrue unless their passphrases are
different. This is how GnuPG itself is designed, where all public &
secret keys are stored together in the home directory, & an identity
is more strongly considered to be the current operating system’s user.
A more effective way a user can separate identities is by setting a
unique home directory for each identity. However, the GnuPG program
wasn’t designed safely as it regards anonymity, so gaining confidence
in its ability to respect more nuanced identity boundaries is dubious
at best.The values that are inserted into raised exceptions were renamed to
be declarative of exactly what has been inserted. I.e., instead of
calling all the inserted exception object attributes something as
generic asvalue, they are nowinputs,uid,output,
etc. This helps improve readability & clarity.Minor ChangesVarious documentation improvements & fixes.Various code cleanups & refactorings.Added type hints to many of the codebase’s parameters.Changes for version 0.7.7Minor ChangesSome documentation improvements & refactorings.Changes for version 0.7.6Major ChangesAdded the newIssueclass. It takes care of raising exceptions
& giving error messages to the user for issues which aren’t caused
by calling the gpg2 binary. This comes with some refactorings.Minor ChangesVarious code cleanups & refactorings.Changes for version 0.7.5Major ChangesNewTerminal,MessageBus&Errorclasses were created to
assist in some heavy refactorings of the codebase. Separating error
handling logic & sending commands to the terminal into their own
classes & methods.Minor ChangesRemoved theimport-drop-uidsoption from the package’s import
commands for several reasons. First, this option doesn’t work on most
systems. Second, if it did work, the result would be problematic, as
that would mean all uid information would always be dropped from
imported keys. This option was intended to keep GnuPG from crashing
when importing keys which don’t have uid information, but it’s an
unideal hack around the root problem.Some changes to function signatures for a better ux, & various code
cleanups.Changes for version 0.7.4Minor ChangesThehomedir,options,executable,_base_command,
&_base_passphrase_commandattributes are now all properties. This
keeps their values in-sync even after a user changes aGnuPGinstance’s configurations. This also backtracks the last update’s
solution of reseting static values after every mutation, to a
solution which reads attributes live as they’re queried. The reduced
efficiency of not using cached values is not noticeable in comparison
to the many milliseconds it takes to run any gpg2 command.Reordering of the methods in theGnuPGclass to better follow a
low-level to high-level overall semantic structure, with positional
groupings of methods which have related functionalities.Some other code refactorings, cleanups & docstring fixes.Changes for version 0.7.3Minor ChangesNow, after either the paths for the executable, homedir or config
file are changed by the user, the_base_command&_base_password_commandstring attributes are reset to mirror those
changes. This keeps the  instance’s state coherent & updated
correctly.Changes for version 0.7.2Minor ChangesChanged the default directory for the gpg executable to/usr/bin/gpg2.
This isn’t going to be appropriate for all users’ systems. But, now
many users on linux installations won’t need to pass in a path
manually to get the package to work.Changes for version 0.7.1Minor ChangesSome interface refactorings for theNetworkclass.Some docstring & readme fixes.Changes for version 0.7.0Major ChangesThe package no longer comes with its own gpg2 binary. TheGnuPGclass
was altered so that a user can set the path to the binary that exists
on their system manually. The path to the config file & to the home
directory can also be set independently now as well. Although, the
home directory & config file still default to the one’s in the
package. These changes should allow users to more easily utilize the
package even if they aren’t using Debian-like operating systems.The interface for theGnuPGclass was also made a bit smaller by
making some methods private.The asynchronous file import & export functions were switched to
synchronous calls. This is a push towards a more sycnhronous focus, as
the gpg2 binary & gpg-agent processes don’t play well with threaded
or truly asynchronous execution. The networking asynchrony will
remain.Heavy refactoring for method names to make the interface more unified
& conherent.TheGnuPGclass now only receives keyword-only arguments. Theusername,email&passphraseparameters no longer use
empty default string values.Removed thenetwork_sks_importmethod which was no longer working.
The onion sks server seems to change its onion address to frequently
to maintain support within the package.CreatedNetwork&Userclasses to better separate concerns
to dedicated & expressive objects.Minor ChangesVarious refactorings.Some bug fixes in the html parsing of the keyserver responses.Changes for version 0.6.1Minor ChangesEdits totest_tiny_gnupg.py.Major ChangesCause of CI build failures found. The sks/pks keyserver’s onion address
was not accessible anymore. They seemed to have switched to a new onion
address available here:http://pgpkeysximvxiazm.onion.Changes for version 0.6.0Minor ChangesChanges to deduce bug causing CI failure.Major ChangesSwitch fromaiohttp_socks’s deprecatedSocksProxyto the newer
and supportedProxyConnector.Changes for version 0.5.9Minor ChangesAdd checks innetwork_sks_import()for html failute sentinels.Major ChangesSpread out the amount of queries per key intest_tiny_gnupg.pyso
the keyserver’s rate limiting policies don’t cause the CI build to
fail as often.Changes for version 0.5.8Minor ChangesFixsetupattribution kwargs insetup.py.Major ChangesAdded newnetwork_sks_import()method which allows users to query the
sks infrastructure for public keys as well. We use an onion address mirror
of the sks/pks network available here:http://jirk5u4osbsr34t5.onion.Added newmanualkwarg tocommandwhich simplifies the process
of using theGnuPG()class to manage gpg2 non-programmatically.
Passingmanual=Truewill allow users to craft commands and interact
directly with the gpg2 interface.Changes for version 0.5.7Minor ChangesTests added to include checks for instance-isolated identities.Major Changesreset_daemon()calls added todecrypt(),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 throwPermissionErrororLookupErrorif 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.6Minor ChangesAdded newly developedauto_decrypt()&auto_encrypt()methods
to theREADME.rsttutorial.Allow keyserver queries with spaces by replacing" "with url
encoding"%20".packet_fingerprint(target="")&list_packets(target="")methods
now raiseTypeErrorwhentargetis clearly not OpenPGP data.Tests added to account for new error handling intiny_gnupg.py.Major Changes--no-ttyseems to keep most of the noise from terminal output while
also displaying important banner information. For instance, signature
verification still produces detailed signature information. Because it
automatically seems to behave as desired, it’s here to stay.Changes for version 0.5.5Minor ChangesAdded to Known Issues. Our package can’t build on Github (Or most any
CI service) for many reasons related their build environments using
Docker & an issue in GnuPG itself.Removed Above known issue as a fix was found for using the Github CI
tool.Added_home,_executable, &_optionsattributes which
store thepathlib.Path.absolute()representation of the associated
files & directories.Addedoptionsattribute with is the str value of the_optionspathlibpath to the configuration file used by the package.Major ChangesAdded"--no-tty"option tocommand()method which conveniently
tells gpg2 not to use the terminal to output messages. This has lead to
a substantial, possibly complete, reduction in the amount of noise gpg2
prints to the screen. Some of that printed information is helpful to
see, though. We would add it back in places where it could be informative,
but passing"--no-tty"has the added benefit of allowing Docker not
to break right out of the gate of a build test. More thought on this
is required.Removedpathlibfrom imports. That module has been in the standard
library since c-python3.4. This package isn’t looking to be supported
for anything older than 3.6.Changes for version 0.5.4Minor ChangesStyle edits toPREADME.rst.Major ChangesFixed a major bug indecrypt()which miscategorized a fingerprint scraped
from a message as the sender’s, when in fact it should be the recipient’s.
Getting the sender’s fingerprint requires successfully decrypting the
message & scraping the signature from inside if it exists. We do this
now, raisingLookupErrorif the signature inside has no corresponding
public key in the package keyring.Added newauto_encrypt()method which follows afterauto_decrypt()in allowing a user to attempt to encrypt a message to a recipient’s
key using the value in theuidkwarg. If there’s no matching key
in the package keyring, then the keyserver is queried for a key
that matchesuidwhere thenmessageis encrypted if found, orFileNotFoundErroris raised if not.Added better exception raising throughout theGnuPGclass:Now, instead of callingread_output()when the supplieduidhas no key in the package keyring, aLookupErroris raised.The best attempt at deriving a 40-byte key fingerprint fromuidis
returned back through theLookupErrorexception object’svalueattribute for downstream error handling.verify()raisesPermissionErrorif verification cannot be
done on themessagekwarg. RaisesLookupErrorinstead if
a public key is needed in order to attempt verification.verifycan’t be used on an encrypted messages in general, unlessmessageis specifcally a signature, not encrypted plaintext. This is just
not how verify works. Signatures are on the inside on encrypted
messages. Sodecrypt()should be used for those instead, it
throws if a signature is invalid on a message.A rough guide now exists for what exceptions mean, since we’ve given
names & messages to the most likely errors, & helper functions
to resolve them. Users can now expect to run into more than just
the in decriptCalledProcessError. Exceptions currently being
used include:LookupError,PermissionError,TypeError,ValueError,KeyError, &FileNotFoundError.ValueErrorraised intext_export()&sign()switched toTypeErroras it’s only raised when theirsecretorkeykwargs, respectively, are not of typebool.Changes for version 0.5.3Minor ChangesFixing PyPiREADME.rstrendering.Changes for version 0.5.2Minor ChangesFuther test cleanups. We’re now at 100% line coverage & 99% branch
coverage.Code cleanups.raw_packets()now passes the uid information it’s
gathered through theKeyErrorexception, in thevalueattribute
instead of copyingsubprocess’soutputattribute naming convention.License, coverage, package version badges added toREADME.rst.Changes for version 0.5.1Minor ChangesFixed inaccuracies & mess-ups in the tests. Added tests for parsing
some legacy keys’ packets withraw_packets().Major ChangesBug in the packet parser has been patched which did not correctly
handle or recognize some legacy key packet types. This patch widens
the pool of compatible OpenPGP versions.Changes for version 0.5.0Minor ChangesRemoved coverage.py html results. They are too big, & reveal device
specific information.Changes for version 0.4.9Minor ChangesVarious code cleanups.Added to test cases for auto fetch methods & packet parsing.Documentation improvements:README.rstedits.CHANGES.rstKnown Issues moved to its own section at the top. Docstrings now
indicate code args & kwargs in restructured text, double tick
format.Addeduse-agentback into the gpg2.conf file to help gnupg to not
open the system pinentry window. This may have implications for
anonymity since multiple instances runnning on a user machine will
be able to use the same agent to decrypt message’s, even if the
decrypting instance wasn’t theintendedrecipient. This may be
removed again. A factor in this decision is that, it’s not clear
whether removing it or addingno-use-agentwould evenhave an impacton the gpg-agent’s decisions._session,_connector,session&connectorcontructors
were renamed to title case, since they are class references or are
class factories. They are now named_Session,_Connector,Session&Connector.Added some functionality tosetup.pyso that thelong_descriptionon PyPI which displays bothREADME.rst&CHANGES.rst, will
also be displayed on github through a combinedREADME.rstfile.
The oldREADME.rstis now renamedPREADME.rst.Major Changes100% test coverage!Fixed bug inraw_packets()which did not return the packet
information when gnupg throws a “no private key” error. Now the
packet information is passed in theoutputattribute of theKeyErrorexception up topacket_fingerprint()andlist_packets(). If another cause is determined for the error, thenCalledProcessErroris raised instead.packet_fingerprint()now returns a 16 byte key ID when parsing
packets of encrypted messages which would throw a gnupg “no private
key” error. The longer 40 byte fingerprint is not available in the
plaintext packets.Newlist_packets()method added to handle the error scraping ofraw_packets()& return thetarget’s metadata information in
a more readable format.Fixed bug informat_list_keys()which did not properly parseraw_list_keys(secret=False)whensecretwas toggled toTrueto display secret keys. The bug would cause the program to falsely
show that only one secret key exists in the package keyring,
irrespective of how many secret keys were actually there.Added a second round of fingerprint finding indecrypt()andverify()to try at returning more accurate results to callers and
in the raised exception’svalueattribute used byauto_decrypt()&auto_verify().Changes for version 0.4.8Minor ChangesFixed typos across the code.Added to test cases.Documentation improvements.CHANGES.mdhas been converted toCHANGES.rstfor easy integration intoREADME.rstandlong_descriptionofsetup.py.README.rsttutorial expanded.Condensed command constructions inset_base_command()andgen_key()by reducing redundancy.Fixeddelete()method’s print noisy output when called on a key
which doesn’t have a secret key in the package’s keyring.Major ChangesAdded asecretkwarg tolist_keys()method which is a boolean
toogle between viewing keys with public keys & viewing keys with
secret keys.Added a reference to the asyncio.get_event_loop().run_until_complete
function in the package. It is now importable withfrom tiny_gnupg import runorfrom tiny_gnupg import *. It
was present in all of the tutorials, & since we haven’t decided to
go either all async or sync yet, it’s a nice helper.Addedraw_packets(target="")method which takes in OpenPGP data,
like a message or key, & outputs the raw terminal output of the--list-packetsoption. Displays very detailed information of all
the OpenPGP metadata ontarget.Addedpacket_fingerprint(target="")method which returns the
issuer fingerprint scraped off of the metadata returned fromraw_packets(target). This is a very effective way to retrieve
uid information from OpenPGP signatures, messages & keys to
determine beforehand whether the associated sender’s key is or isn’t
already in the package’s keyring.Changes for version 0.4.7Minor ChangesFixed typos across the code.Added to test cases.Added tests explanation intest_tiny_gnupg.py.Documentation improvements.Major ChangesAdded exception hooks todecrypt()&verify()methods. They
now raiseKeyErrorwhen the OpenPGP data they’re verifying
require a signing key that’s not in the package’s keyring. The
fingerprint of the required key is printed out & stored in thevalueattribute of the raised exception.Added newauto_decrypt()&auto_verify()async methods
which catch the new exception hooks to automatically try a torified
keyserver lookup before raising a KeyError exception. If a key is
found, it’s downloaded & an attempt is made to verify the data.Changes for version 0.4.6Minor ChangesAdded to test cases.Changed the project long description in theREADME.rst.Added docstrings to all the methods in theGnuPGclass, & the
class itself.Major ChangesTurned off options in gpg2.confrequire-cross-certificationandno-commentbecause one or both may be causing a bug where using
private keys raises an “unusable private key” error.Changes for version 0.4.5Minor ChangesUpdated package metadata files to be gpg2.conf aware.Major ChangesAdded support for a default package-wide gpg2.conf file.Changes for version 0.4.4Minor ChangesAdded new tests. We’re at 95% code coverage.Major ChangesChanged the default expiration date on generated keys from never to 3
years after created. This is both for the integrity of the keys, but
also as a courtesy to the key community by not recklessly creating
keys that never expire.Addedrevoke(uid)method, which revokes the key with matchinguidif the secret key is owned by the user & the key passphrase
is stored in the instance’spassphraseattribute.Changes for version 0.4.3Minor ChangesChanged package description to name more specifically the kind of ECC
keys this package handles.Removed the trailing newline character that was inserted into the end
of everyencrypt()&sign()message.Added new tests.Major ChangesFixed 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.2Minor ChangesAdded some keyword argument names toREADME.rsttutorials.Added section inREADME.rstabout torification.Major ChangesAdded a check inencrypt()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 newtorify=Falsekwarg to__init__()which prepends"torify"to each gpg2 command if set toTrue. This will make
sure that if gnupg makes any silent connections to keyservers or the
web, that they are run through tor & don’t expose a users ip
address inadvertently.Changes for version 0.4.1Minor ChangesFixed typos intiny_gnupg.py.Changes for version 0.4.0Minor ChangesAdded keywords tosetup.pyAdded copyright notice to LICENSE file.Code cleanups.UpdatedREADME.rsttutorials.Added new tests.Include .gitignore in MANIFEST.in for PyPI.Made all path manipulations more consistent by strictly using
pathlib.Path for directory specifications.Added strict truthiness avoidance tosign()for thekeyboolean kwarg.Added strict truthiness avoidance totext_export()for thesecretboolean kwarg.Major ChangesAddedkeykwarg to thesign(target="",key=False)method to
allow users to toggle between signing arbitrary data & signing a
key in the package’s local keyring.Changed themessagekwarg insign(message="")totargetso it is also accurate when the method is used to sign keys instead
of arbitrary data.Changes for version 0.3.9Minor ChangesAdded new tests.Major ChangesFixed new crash caused by--batchkeyword inencrypt(). When
a key being used to encrypt isn’t ultimately trusted, gnupg raises an
error, but this isn’t a desired behavior. So,--batchis removed
from the command sent from the method.Changes for version 0.3.8Minor ChangesAdded new tests.Removedbase_command()method because it was only a layer of
indirection. It was merged intocommand().Major ChangesAdded the--batch,--quiet&--yesarguments to the
default commands contructed by thecommand()method.Added the--quiet&--yesarguments to the command
constructed internally to thegen_key()method.Added a general uid —> fingerprint uid conversion indelete()to
comply with gnupg limitations on how to call functions that
automatically assume yes to questions. The Up-shot is thatdelete()is now fully automatic, requiring no user interaction.Changes for version 0.3.7Minor ChangesAdded new tests.Typos & inaccuracies fixed around the code & documentation.Major ChangesAdded newsecretkwargs totext_export(uid, secret=bool)andfile_export(path, uid, secret=bool)to allow secret keys to be
exported from the package’s environment.Added newpost(url, **kw)&get(url, **kw)methods to allow
access to the networking tools without having to manually construct
thenetwork_post()&network_get()context managers. This
turns network calls into one liners that can be more easily wrapped
with an asynciorunfunction.Changes for version 0.3.6Minor ChangesAdded new tests for networking methods.Documentation updates & accuracy fixes.Major ChangesRemoved a check innetwork_import()which wasn’t useful and
should’ve been causing problems with imports, even though the tests
didn’t seem to notice.Changes for version 0.3.5Minor ChangesSwitched the aiocontext package license with the license for
asyncio-contextmanager.Major ChangesThe packaging issues seem to be resolved. Packaging as v0.3.5-beta,
the first release that did not ship completely broken through pip
install –user tiny_gnupg.Changes for version 0.3.4Major ChangesFixing a major bug in the parameters passed tosetup()which did
not correctly tell setuptools to package the gpghome folder & gpg2
binary. This may take a few releases to troubleshoot & bug fix
fully.Changes for version 0.3.3Major ChangesFixed a big bug where the wrong package was imported with the same
name as the intended module. AioContext was imported in setuptools,
but the package that is needed is asyncio-contextmanager for its
aiocontext module. This lead to the program being un-runable due to
an import error.Changes for version 0.3.2Minor ChangesRolled back the changes intrust()that checked for trust levels
on keys to avoid sending an unnecessary byte of data through the
terminal. Mostly because the attempted fix did not fix the issue. And
the correct fix involves a wide branching of state & argument
checking. That runs contrary to the goal of the package for
simplicity, so it isn’t going to be addressed for now.Edited some of theREADME.rsttutorials.Major ChangesFix bug infile_import()method where await wasn’t called on the
keyfile.read() object, leading to a crash.Changes for version 0.3.1Minor ChangesFixed a bug intrust()which caused an extrab“y\n”to be sent to the interactive prompt when setting keys as anything
but ultimately trusted. This was because there’s an extra terminal
dialog asking for a “y” confirmation that is not there when a key is
being set as ultimately trusted. This didn’t have a serious effect
other than displaying a “Invalid command (try ‘help’)” dialog.Removedlocal_userkwarg from theraw_list_keys()andtrust()methods, as it doesn’t seem to matter which “user”
perspective views the list of keys or modifies trust. It is very
likely always displaying keys from the perspective of the global
agent.Typos, redundancies & naming inaccuracies fixed around the code and
documentation.Tests updated & added to.Major ChangesFixed a bug inencrypt()which caused a“y\n”to be
prepended to plaintext that was sent to ultimately trusted keys. This
was because there’s an extra terminal dialog asking for a “y”
confirmation that is not there when a key is ultimately trusted.Added akey_trust(uid)method to allow easy determination of
trust levels set on keys in the local keyring.Changes for version 0.3.0Minor ChangesChanged MANIFEST.in to a more specific include structure, & a
redundant exclude structure, to more confidently keep development
environment key material from being uploaded during packaging.Major ChangesOverhauled thegen_key()which now creates a different set of
default keys. We are no longer creating one primary key which does
certifying & signing, with one subkey which handles encryption.
Instead, we create one certifying primary key, with three subkeys,
one each for handling encryption, authentication, & signing. This
is a more theoretically secure default key setup, & represents a
common best-practice.Changes for version 0.2.9Minor ChangesEdited some of theREADME.rsttutorialsChangedfile_import()’sfilenamekwarg topathfor
clarity.Fixed bug intrust()which would allow a float to be passed to
the terminal when an integer was needed.Changed the way the email address in displayed innetwork_export(), removing the surrounding list brackets.Changed the FILE_PATH global to HOME_PATH for clarity.Changed theid_linkvariable innetwork_import()tokey_urlfor clarity.Major ChangesFixed a bug informat_list_keys()which would imporperly split
the output string when uid information contained the"pub"string.Changes for version 0.2.8Minor ChangesEdited some of theREADME.rsttutorials.Major ChangesFixed a bug in thetrust()method which caused it to never
complete execution.Fixed a bug in thetrust()method which falsely made 4 the
highest trust level, instead of 5.Changes for version 0.2.7Minor ChangesFixed statement inREADME.rstdescribing bug #T4393.Changes for version 0.2.6Minor ChangesTypos, redundancies & naming inaccuracies fixed around the code and
documentation.Added a new POST request tutorial to theREADME.rst.Added"local_user"kwarg to some more methods where the output
could at least be partially determined by the point of view of the
key gnupg thinks is the user’s.Major ChangesAdded a signing toggle to theencrypt(sign=True)method. Now, the
method still automatically signs encrypted messages, but users can
choose to turn off this behavior.Added atrust(uid="",level=4)method, which will allow users to
sign keys in their keyring on a trust scale from 1 to 4.Fixed a bug inset_fingerprint(uid="")which mistakenly used anemailparameter instead of the locally availableuidkwarg.Changes for version 0.2.5Minor ChangesTypos, redundancies & naming inaccuracies fixed around the code and
documentation.Tests updated & added to.Changedraw_network_export()&raw_network_verify()methods
intoraw_api_export()&raw_api_verify(), respectively.
This was done for more clarity as to what those methods are doing.Major ChangesAddedsign(message)&verify(message)methods.Changed thekeyserver&searchserverattributes into
properties so that customportattribute changes are now
reflected in the constructed url, & the search string used by a
custom keyserver can also be reflected.Moved all command validation to theread_output()method which
simplifies the construction ofcommand()& will automaticallyshlex.quote()all commands, even those hard-coded into the
program.Fixed bug inset_homedir()which did not construct the default
gpghome directory string correctly depending on where the current
working directory of the calling script was.Addedlocal_userkwarg toencrypt()&sign()so a user
can specify which key to use for signing messages, as gnupg
automatically signs with whatever key it views as the default user
key. Instead, we assume mesasges are to be signed with the key
associated with the email address of a GnuPG class instance, or the
key defined by thelocal_useruid if it is passed.Fixed –list-keys terminal output parsing. We now successfully parse
& parameterize the output into email addresses & fingerprints, of
a larger set of types of keys.Addeddelete()method for removing both public & private keys
from the local keyring. This method still requires some user
interaction because a system pinentry-type dialog box opens up to
confirm deletion. Finding a way to automate this to avoid user
interaction is in the work.Added automating behavior to thesign()&encrypt()methods
so that keys which haven’t been verified will still be used. This is
done by passing “y” (yes) to the terminal during the process of the
command.Changes for version 0.2.4Minor ChangesUpdatedsetup.pywith more package information.Typos, redundancies & naming inaccuracies fixed around the code and
documentation.Tests updated & added to.Changes for version 0.2.3Minor ChangesTypos & naming inaccuracies fixed around the code and
documentation.Added package togit repoAdded git repo url tosetup.py.Theportattribute is currently unused. It may be removed if it
remains purposeless.Changes for version 0.2.2Minor ChangesTypos & naming inaccuracies fixed around the code and
documentation.Switched the internal networking calls to use the higher levelnetwork_get()&network_post()methods.Removed redundantshlex.quote()calls on args passed to thecommand()method.Tests updated & added to.Changes for version 0.2.1Minor ChangesThe names of some existing methods were changed.parse_output()is nowread_output().gpg_directory()is nowformat_homedir(). The names of some existing attributes were
changed.gpg_pathis nowexecutable, with its parent folder
uri now stored inhome.key_idis nowfingerprintto
avoid similarities with the naming convention used for the methods
which query the package environment keys for uid information,
i.e.key_fingerprint()&key_email().Major ChangesGood riddance to the pynput library hack! We figured out how to
gracefully send passphrases & other inputs into the gpg2
commandline interface. This has brought major changes to the package,
& lots of increased functionality.Many added utilities:Keys generated with thegen_key()method now get stored in a
local keyring instead of the operating system keyring.aiohttp, aiohttp_socks used to power the keyserver queries and
uploading features. All contact with the keyserver is done over
tor, with async/await syntax.search(uid)to query for a key
with matches to the supplied uid, which could be a fingerprint or
email address.network_import(uid)to import a key with
matches to the supplied uid.network_export(uid)to upload a
key in the package’s keyring with matches to the supplied uid to
the keyserver. Also, raw access to the aiohttp.ClientSession
networking interface is available by usingasync with instance.session as session:. More info is
available in theaiohttp docsNewtext_import(key),file_import(filename),text_export(key), &file_export(path, uid)methods for
importing & exporting keys from key strings or files.Newreset_daemon()method for refreshing the system gpg-agent
daemon if errors begin to occur from manual deletion or
modification of files in the package/gpghome/ directory.Newencrypt(message, recipient_uid)&decrypt(message)methods. Theencrypt()method automatically signs the message,
therefore needs the key passphrase to be stored in thepassphraseattribute. The same goes for thedecrypt()method.Thecommand(*options),encode_inputs(*inputs), andread_output(commands, inputs)methods can be used to create
custom commands to the package’s gpg2 environment. This allows for
flexibility without hardcoding flexibility into every method,
which would increase code size & complexity. Thecommand()method takes a series of options that would normally be passed to
the terminal gpg2 program (such as –encrypt) & returns a list
with those options included, as well as, the other boiler-plate
options (like the correct path to the package executable, & the
package’s local gpg2 environment.).encode_inputs()takes a
series of inputs that will be needed by the program called with
thecommand()instructions, &bytes()encodes them with
the necessary linebreaks to signal separate inputs.read_output()takes the instructions fromcommand()and
inputs fromencode_inputs()& callssubprocess.check_output(commands,input=inputs).decode()on
them to retrieve the resulting terminal output.