Package: gpg-lite
gpg-lite: a cross-platform python binding for GnuPGgpg-liteis a python API forGPG / GnuPGthat offers
the following functionalities:PGP key management: create and delete keys. Search for keys in the local
keyring.Data encryption: encrypt and sign files.Data decryption: decrypt files.Signature verification: verify signatures attached to files.Keyserver interactions: query, download and upload keys to keyservers.The design objectives behindgpg-liteare the following:Cross-plateform: support for 🐧, 🍏, Windows.Multi-version: support as many gpg versions as possible.Provide custom functions for keyserver access, as the behavior of the gpg
binary is inconsistent across versions.Reduce functionality to the most useful and frequent operations to reduce
maintenance costs.InstallationFromPyPIIt's simple, just do:[sudo]pipinstall[--user]gpg-liteFrom gitTo installgpg-litefrom this git repository:gitclonehttps://gitlab.com/biomedit/gpg-lite.gitcdgpg-lite
./setup.pyinstall[--user]Using gpg-liteNote that in order to usegpg-liteyou will need to haveGnuPGinstalled on your local machine.To get started usinggpg-liteimport the module as follows:importgpg_liteasgpgMost of the functionality ofgpg-liteis provided via methods of theGPGStoreobject class. This class aims to represents an instance of aGnuPG home directory, the directory on your local machine that contains
the GPG keyrings and other related database files.Here is how aGPGStoreinstance is created if your GnuPG installation isstandard. Herestandardmeans that your GnuPG executable is named eithergpgorgpg2, that it is part of your PATH, and that the GnuPG home
directory is at its default location (e.g.~/.gnupgon Linux):gpg_store=gpg.GPGStore()print("The GnuPG home directory is set to:",gpg_store.gnupg_home_dir)print("The GnuPG executable is:",gpg_store.gnupg_binary)Fornon-standardGnuPG installations, the optionalgnupg_home_dirandgnupg_binaryarguments ofGPGStore()can be used: the former allows to
specify the path of a custom GnuPG home directory, while the later allows to
specify a non-standard GnuPG executable name and/or path. Using a leading~character in thegnupg_home_dirargument is supported and expands to the
user's home directory.gpg_store=gpg.GPGStore(gnupg_home_dir="~/custom/path/gnupg_home_dir",gnupg_binary="/usr/bin/gpg")print("The GnuPG home directory is set to:",gpg_store.gnupg_home_dir)print("The GnuPG executable is:",gpg_store.gnupg_binary)Generate a new keygpg_store=gpg.GPGStore()key_fingerprint=gpg_store.gen_key(key_type='RSA',key_length=4096,full_name="Chuck Norris",email="chuck.norris@roundhouse.gov",passphrase="Chuck Norris does not need one - the password needs him")print("Created new key with fingerprint:",key_fingerprint)Retrieve public and secret keys from local keyringRetrieving one or more keys from the local keyring is done using thelist_pub_keys()(for public keys) andlist_sec_keys()(for secret/private
keys) methods:If nosearch_termsargument is passed, all keys are returned.If an iterable - typically a tuple or a list - of strings is passed assearch_termsargument, only keys whose fingerprint, key ID or user ID
matches one of the items in the iterable are returned.
Note that passing directly a string tosearch_termsis unlikely to return
the expected keys, as individual characters in the string will be iterated
over and matched).Important:if multiple matching keys are found, they arenotreturned
in any particular order.When usinglist_pub_keys(), thesigs=Trueargument can be optionally
passed to return keys with their signatures.keys=gpg_store.list_pub_keys()forkeyinkeys:print(key.uids[0],key.fingerprint)key=gpg_store.list_pub_keys(search_terms=("chuck.norris",))[0]key=gpg_store.list_pub_keys(search_terms=("chuck.norris@roundhouse.gov",),sigs=True)[0]fingerprint=key.fingerprintkey=gpg_store.list_pub_keys(search_terms=(fingerprint,),sigs=True)[0]Export public and secret keys from local keyringKeys can be exported in both ascii-armored and binary format usingexport()andexport_secretmethods.Important: secret keys, when exported, are encrypted with the provided password.
However, it is critical to store them in a safe, private space.fingerprint="55C5314BB9EFD19AE7CC4774D892C41917B20115"pub_key_ascii=gpg_store.export(fingerprint,armor=True)pub_key_binary=gpg_store.export(fingerprint,armor=False)priv_key_ascii=gpg_store.export_secret(fingerprint,passphrase="secret",armor=True)priv_key_binary=gpg_store.export_secret(fingerprint,passphrase="secret",armor=False)Encrypt and sign dataData decryption is performed using theencrypt()method of theGPGStoreclass.importtempfileencrypted_file=tempfile.NamedTemporaryFile(prefix="gpg-lite_test_",suffix=".gpg",delete=False).namewithopen(encrypted_file,"w")asf:gpg_store.encrypt(source=b"When Chuck Norris throws exceptions, it's across the room.\n",recipients=["chuck.norris@roundhouse.gov"],output=f)Optionally, the encrypted file can also be signed with a private key. This
requires to pass the passphrase associated with the private key.withopen(encrypted_file,"w")asf:gpg_store.encrypt(source=b"When Chuck Norris throws exceptions, it's across the room.\n",recipients=["chuck.norris@roundhouse.gov"],output=f,sign="chuck.norris@roundhouse.gov",passphrase="Chuck Norris does not need one - the password needs him")Please seehelp(gpg.GPGStore.encrypt)for more details.Decrypt dataData decryption is performed using thedecrypt()method of theGPGStoreclass. Here is an example, reading from the file we encrypted earlier, and
outputting the result to a new unencrypted file:decrypted_file=encrypted_file[:-4]withopen(encrypted_file,"rb")asf,open(decrypted_file,"w")asf_out:gpg_store.decrypt(source=f,output=f_out,passphrase="Chuck Norris does not need one - the password needs him")withopen(decrypted_file,"r")asf:print("Decrypted message:","".join(f.readlines()))# Delete files created in this demo:importosos.remove(encrypted_file)os.remove(decrypted_file)Make and verify detached signaturesA so-called detached signature is a file that contains information to verify
both the hash of the file who was signed and the signee, i.e. the person who
signed the file.Detached signatures can be created as follows:withtempfile.NamedTemporaryFile(delete=False)asinput_file,\tempfile.NamedTemporaryFile(delete=False)assigned_file:# Create a new file with some text.withopen(input_file.name,mode="w")asf:f.write("When Chuck Norris throws exceptions, it's across the room.\n")# Sign the existing file. Note that the file must be opened in binary mode.withopen(input_file.name,mode="rb")asf_in,open(signed_file.name,mode="wb")asf_out:withgpg_store.detach_sig(src=f_in,signee=fingerprint,passphrase="Chuck Norris does not need one - the password needs him")asstream:f_out.write(stream.read())# Verify the signature.withopen(input_file.name,mode="rb")asf,open(signed_file.name,mode="rb")asf_signature:signee_fingerprint=gpg_store.verify_detached_sig(src=f,sig=f_signature.read())print("Signee fingerprint is",signee_fingerprint)Same as above, but using a bytestring as input instead of a file:content_to_sign=b"Chuck Norris doesn't use web standards. The web conforms to him.\n"withgpg_store.detach_sig(src=content_to_sign,signee=fingerprint,passphrase="Chuck Norris does not need one - the password needs him")asstream:signee_fingerprint=gpg_store.verify_detached_sig(src=content_to_sign,sig=stream.read())print("Signee fingerprint is",signee_fingerprint)Delete keys from local keyringPublic and secret/private keysWarning:deleted secret keys cannot be recovered or re-generated from their
public counterpart. Only delete secret keys if you are sure you will never
need them again.key=gpg_store.list_pub_keys(search_terms=("chuck.norris@roundhouse.gov",),sigs=True)[0]fingerprint=key.fingerprintgpg_store.delete_sec_keys(fingerprint)gpg_store.delete_pub_keys(fingerprint)Specific exceptions raised by gpg-litegpg-litehas the following exceptions:GPGError: exception raised when the local GnuPG executable returns an error.KeyserverError: exception that is raised when a specified keyserver is not
responding, e.g. because a wrong URL was given or the keyserver is currently
not available.KeyserverKeyNotFoundError: exception that is raised when given key is not
found on a specified keyserver.Bug Reports / Feature Requests / ContributingOur bug tracker can be found onGitLabhttps://gitlab.com/biomedit/gpg-lite/issues.Public comments and discussions are also welcome on the bug tracker.Patches are always welcome 🤗!
Take into account that we use a special format for commit messages.
This is due to our release management and to auto generate our
changelog.
Here are ourguidelines.
Also each change has to pass our CIpipelineincluding:blackcode formattingpylintlintsunit/integrationtestsbanditvulnerability checks (each security warning which cannot be
avoided has to be justified)mypytype checkingSupported GPG versionsWe officially support allGPGversions starting fromv2.2.8.
Unofficially, we also try to supportv2.0.22.Running the unit testsRun the following commands ingpg-lite's root directory. To also run tests
that require the presence ofGnuPGon the machine running the tests, set theWITH_GPG=trueenvironment variable.python3-munittestdiscover-s./test-vWITH_GPG=truepython3-munittestdiscover-s./test-v