added symmetric encryption. changed options in to groups

This commit is contained in:
Q
2024-06-27 18:44:22 +03:00
parent 1bae716f07
commit 2c5965d164
2 changed files with 137 additions and 48 deletions

View File

@@ -19,25 +19,30 @@ install_useve() {
py-format .
useve qgpg
pip install ./
qgpg --help
}
test_encrypt() {
# Prepare
cd ~/tmp/
. useve-runner
useve qgpg
rm -fr key2* datadir sha1sum.txt
GPGPASS=secret @ qgpg --key key2 k
rm -fr key2* datadir datadir.encrypted sha1sum.txt
@ mkdir -p datadir/folder{1,2}
@ dd if=/dev/random of=datadir/testfile bs=3024 count=102400
for i in {1..10}; do
dd if=/dev/random of=datadir/folder1/${i}testfile bs=1024 count=10240 &> /dev/null
dd if=/dev/random of=datadir/folder2/${i}testfile bs=1024 count=10240 &> /dev/null
done
@ qgpg --key key2.pub e datadir/folder1/1testfile
@ qgpg --key key2.pub e datadir/folder1/1testfile datadir/1testfile.encrypted.gpg
@ qgpg --key key2.pub -r e datadir
@ qgpg --key key2.pub -r e datadir
@ qgpg --key key2.pub -r e $(pwd)/datadir/ datadir.encrypted/
# Run encryption code
set -e
GPGPASS=secret @ qgpg -k --key key2
@ qgpg -e --key key2.pub datadir/folder1/1testfile
@ qgpg -e --key key2.pub datadir/folder1/1testfile datadir/1testfile.encrypted.gpg
@ qgpg -e --key key2.pub -r datadir
@ qgpg -e --key key2.pub -r datadir
@ qgpg -e --key key2.pub -r $(pwd)/datadir/ datadir.encrypted/
GPGPASS=symmetric_password @ qgpg -e datadir/folder1/1testfile datadir/1testfile.encrypted.symmetric.gpg
@ hash-update -t sha1 -f sha1sum.txt -r datadir
}
@@ -45,8 +50,10 @@ test_decrypt() {
cd ~/tmp/
. useve-runner
useve qgpg
set -e
@ find datadir -type f -name '*testfile' | xargs -II rm -v I
GPGPASS=secret @ qgpg --key key2 -r d datadir
GPGPASS=secret @ qgpg -d --key key2 -r datadir
GPGPASS=symmetric_password @ qgpg -d --force datadir/1testfile.encrypted.symmetric.gpg datadir/folder1/1testfile
@ hash-update -t sha1 -f sha1sum.txt -c
}

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
__version__ = "1.1.1"
__version__ = "20240627.1"
import argparse
import os
@@ -101,52 +101,106 @@ def get_opts():
"""Returns command line arguments"""
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
add_help=False,
description="Wrapper to GPG, allowing recursive encryption.",
epilog="""Private key passphrase can be set to env variable GPGPASS, otherwise will prompt
epilog="""Private key passphrase can be set to env variable GPGPASS, otherwise program will prompt it.
Usage
=====
- Generate keys:
qgpg --key ./path/mykey --name "User Name" keygen
qgpg --keygen --key ./path/mykey --name "User Name"
- This will create private and public keys, mykey and mykey.pub
- Share the public key to someone who will encrypt data to you
- Encrypt file:
qgpg --key ./mykey.pub encrypt ./plain_file.txt ./encrypted_file.txt.gpg
- Encrypt all files in a folder:
qgpg --key ./mykey.pub -r encrypt ./folder/
qgpg --encrypt --key ./mykey.pub ./plain_file.txt ./encrypted_file.txt.gpg
- Encrypt all files recursively in a folder:
qgpg --encrypt --key ./mykey.pub --recursive ./in_folder/
qgpg --encrypt --key ./mykey.pub --recursive ./from_folder/ ./to_another_folder/
- Decrypt all files with the private key:
qgpg --key ./mykey -r decrypt ./encrypted_folder/
qgpg --decrypt --key ./mykey --recursive ./encrypted_folder/
- Using passhprase in a variable:
GPGPASS=mysecretpassword qgpg --key ./mykey decrypt file.gpg file.txt
GPGPASS=mysecretpassword qgpg --key ./mykey --decrypt file.gpg file.txt
- Symmetric encryption (Just leave out the --key):
qgpg --encrypt file.txt file.gpg
""",
)
parser.add_argument("--width", help="Console width in characters. Defaults to auto detect.")
parser.add_argument("--no-progress", help="Disable progress meter.", default=False, action="store_true")
parser.add_argument(
"--key", help="path to recipient public key for encryption or private key for decryption", required=True
commands = parser.add_argument_group("Command", "Main command for the program")
commands_grp = commands.add_mutually_exclusive_group(required=True)
commands_grp.add_argument(
"--keygen",
"-k",
default=False,
action="store_true",
help="Create new key pair",
)
parser.add_argument("--name", help="Name of the owner of the key", required=False, default="recipient@address")
parser.add_argument(
commands_grp.add_argument(
"--encrypt",
"-e",
default=False,
action="store_true",
help="Encrypt files",
)
commands_grp.add_argument(
"--decrypt",
"-d",
default=False,
action="store_true",
help="Decrypt files",
)
crypt_grp = parser.add_argument_group("Encryption", "Encryption and Decryption options")
crypt_grp.add_argument(
"--key",
help="Path to key file. For keygen: private key to write; encrypt: public key file, decrypt: private key file. If not defined, symmetric password encryption is used.",
)
keygen_grp = parser.add_argument_group(
"Keygen", "Options for keygen. Note: --key is required for keygen, see above."
)
keygen_grp.add_argument(
"--name",
help="Name of the owner of the key (usually email) Default: %(default)s",
required=False,
default="recipient@address",
)
crypt_grp.add_argument(
"--recursive",
"-r",
default=False,
action="store_true",
help="Recursive encryption or decryption. Processes full folder structures",
)
parser.add_argument(
"command",
help="keygen/encrypt/decrypt. you can also use just the first letter.",
choices=["k", "e", "d", "keygen", "encrypt", "decrypt"],
crypt_grp.add_argument(
"--force",
default=False,
action="store_true",
help="Overwrite existing files",
)
parser.add_argument("path", help="path to file to encrypt/decrypt, or folder if recursive", nargs="?")
parser.add_argument("out_path", help="output file or folder when using recursive.", nargs="?")
misc_grp = parser.add_argument_group("Misc", "Other options")
misc_grp.add_argument("--width", help="Console width in characters. Defaults to auto detect.")
misc_grp.add_argument("--no-progress", help="Disable progress meter.", default=False, action="store_true")
misc_grp.add_argument("--version", help="Display version and exit", default=False, action="store_true")
misc_grp.add_argument("--help", "-h", action="help", help="Show this help message and exit")
crypt_grp.add_argument("path", help="Path to source file to encrypt/decrypt, or folder if recursive", nargs="?")
crypt_grp.add_argument(
"out_path",
help="Path to target file or folder when using recursive. If not specified, use of .gpg extension is automatically used.",
nargs="?",
)
parsed = parser.parse_args()
for cmd in ("keygen", "encrypt", "decrypt"):
if parsed.command == cmd[0]:
parsed.command = cmd
if parsed.version:
print(__version__)
sys.exit(0)
for cmd in zip(("keygen", "encrypt", "decrypt"), (parsed.keygen, parsed.encrypt, parsed.decrypt)):
if cmd[1]:
parsed.command = cmd[0]
if parsed.command == "keygen":
if parsed.key is None:
parser.error("--key required for keygen")
return parsed
@@ -171,26 +225,33 @@ class Processor:
self.homedir = tempfile.TemporaryDirectory()
self.gpg = gnupg.GPG(gnupghome=self.homedir.name)
self.gpg.encoding = "utf-8"
self.symmetric = self.opts.key is None
self.suffix = ".gpg"
self.phrase = None
if self.opts.command == "keygen":
self.keygen()
filelist = []
if self.opts.command in ("encrypt", "decrypt"):
import_result = self.gpg.import_keys_file(self.opts.key)
if not self.symmetric:
import_result = self.gpg.import_keys_file(self.opts.key)
filelist = self.get_filelist(self.opts.path, self.opts.recursive, self.opts.command)
if self.opts.command == "encrypt":
keyid = import_result.fingerprints[0]
public_keys = self.gpg.list_keys()
for key in public_keys:
if key["fingerprint"] == keyid:
self.uid = key["uids"][0]
if self.symmetric:
self.set_phrase(twice=True)
else:
keyid = import_result.fingerprints[0]
public_keys = self.gpg.list_keys()
for key in public_keys:
if key["fingerprint"] == keyid:
self.uid = key["uids"][0]
if self.opts.command == "decrypt":
self.set_phrase()
n_f = len(filelist)
for i, f in enumerate(filelist):
print(f"{i+1}/{n_f} {self.opts.command}ing {f} ", file=sys.stderr)
@@ -200,16 +261,27 @@ class Processor:
def set_phrase(self, twice=False):
if not self.phrase is None:
# phrase already set
return
self.phrase = os.environ.get("GPGPASS", None)
if self.phrase is None:
print("Type private key passphrase [Empty if no passphrase]")
print(
"Type symmetric passphrase"
if self.symmetric
else "Type private key passphrase [Empty if no passphrase]"
)
self.phrase = getpass()
if twice:
print("Type private key passphrase again to confirm")
if twice and len(self.phrase) > 0:
print("Type passphrase again to confirm")
phrase2 = getpass()
if phrase2 != self.phrase:
print("Passphrases do not match!")
sys.exit(1)
if self.phrase == "" and self.symmetric:
print("Symmetric passphrase required!")
sys.exit(1)
def get_filelist(self, root, recurse, direction):
if not recurse:
@@ -229,9 +301,12 @@ class Processor:
return filelist
def keygen(self):
if os.path.exists(self.opts.key):
print(f"File {self.opts.key} already exists")
sys.exit(1)
if not self.opts.force:
for f in (self.opts.key, self.opts.key + ".pub"):
if os.path.exists(f):
print(f"File {f} already exists")
sys.exit(1)
print(f"Generating keys for {self.opts.name}...", file=sys.stderr)
self.set_phrase(twice=True)
input_data = self.gpg.gen_key_input(
@@ -264,9 +339,11 @@ class Processor:
os.makedirs(os.path.dirname(out_file), exist_ok=True)
else:
out_file = self.opts.out_path if self.opts.out_path else auto_path
if os.path.exists(out_file):
print(f"{out_file} already exists", file=sys.stderr)
return
if not self.opts.force:
if os.path.exists(out_file):
print(f"{out_file} already exists", file=sys.stderr)
return
file_size = os.path.getsize(in_file)
if not self.opts.no_progress:
@@ -274,7 +351,12 @@ class Processor:
try:
with open(in_file, "rb") as fp:
if self.opts.command == "encrypt":
d = self.gpg.encrypt_file(fp, self.uid, always_trust=True, armor=False)
if self.symmetric:
d = self.gpg.encrypt_file(
fp, None, symmetric="AES256", passphrase=self.phrase, always_trust=True, armor=False
)
else:
d = self.gpg.encrypt_file(fp, self.uid, always_trust=True, armor=False)
if self.opts.command == "decrypt":
d = self.gpg.decrypt_file(fp, passphrase=self.phrase, always_trust=True)
if not d.ok: