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

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
__version__ = "1.1.1" __version__ = "20240627.1"
import argparse import argparse
import os import os
@@ -101,52 +101,106 @@ def get_opts():
"""Returns command line arguments""" """Returns command line arguments"""
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter, formatter_class=argparse.RawDescriptionHelpFormatter,
add_help=False,
description="Wrapper to GPG, allowing recursive encryption.", 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 Usage
===== =====
- Generate keys: - 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 - This will create private and public keys, mykey and mykey.pub
- Share the public key to someone who will encrypt data to you - Share the public key to someone who will encrypt data to you
- Encrypt file: - Encrypt file:
qgpg --key ./mykey.pub encrypt ./plain_file.txt ./encrypted_file.txt.gpg qgpg --encrypt --key ./mykey.pub ./plain_file.txt ./encrypted_file.txt.gpg
- Encrypt all files in a folder: - Encrypt all files recursively in a folder:
qgpg --key ./mykey.pub -r encrypt ./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: - 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: - 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.") commands = parser.add_argument_group("Command", "Main command for the program")
parser.add_argument("--no-progress", help="Disable progress meter.", default=False, action="store_true") commands_grp = commands.add_mutually_exclusive_group(required=True)
parser.add_argument( commands_grp.add_argument(
"--key", help="path to recipient public key for encryption or private key for decryption", required=True "--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") commands_grp.add_argument(
parser.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", "--recursive",
"-r", "-r",
default=False, default=False,
action="store_true", action="store_true",
help="Recursive encryption or decryption. Processes full folder structures", help="Recursive encryption or decryption. Processes full folder structures",
) )
crypt_grp.add_argument(
parser.add_argument( "--force",
"command", default=False,
help="keygen/encrypt/decrypt. you can also use just the first letter.", action="store_true",
choices=["k", "e", "d", "keygen", "encrypt", "decrypt"], help="Overwrite existing files",
) )
parser.add_argument("path", help="path to file to encrypt/decrypt, or folder if recursive", nargs="?") misc_grp = parser.add_argument_group("Misc", "Other options")
parser.add_argument("out_path", help="output file or folder when using recursive.", nargs="?") 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() parsed = parser.parse_args()
for cmd in ("keygen", "encrypt", "decrypt"): if parsed.version:
if parsed.command == cmd[0]: print(__version__)
parsed.command = cmd 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 return parsed
@@ -171,18 +225,24 @@ class Processor:
self.homedir = tempfile.TemporaryDirectory() self.homedir = tempfile.TemporaryDirectory()
self.gpg = gnupg.GPG(gnupghome=self.homedir.name) self.gpg = gnupg.GPG(gnupghome=self.homedir.name)
self.gpg.encoding = "utf-8" self.gpg.encoding = "utf-8"
self.symmetric = self.opts.key is None
self.suffix = ".gpg" self.suffix = ".gpg"
self.phrase = None
if self.opts.command == "keygen": if self.opts.command == "keygen":
self.keygen() self.keygen()
filelist = [] filelist = []
if self.opts.command in ("encrypt", "decrypt"): if self.opts.command in ("encrypt", "decrypt"):
if not self.symmetric:
import_result = self.gpg.import_keys_file(self.opts.key) import_result = self.gpg.import_keys_file(self.opts.key)
filelist = self.get_filelist(self.opts.path, self.opts.recursive, self.opts.command) filelist = self.get_filelist(self.opts.path, self.opts.recursive, self.opts.command)
if self.opts.command == "encrypt": if self.opts.command == "encrypt":
if self.symmetric:
self.set_phrase(twice=True)
else:
keyid = import_result.fingerprints[0] keyid = import_result.fingerprints[0]
public_keys = self.gpg.list_keys() public_keys = self.gpg.list_keys()
for key in public_keys: for key in public_keys:
@@ -191,6 +251,7 @@ class Processor:
if self.opts.command == "decrypt": if self.opts.command == "decrypt":
self.set_phrase() self.set_phrase()
n_f = len(filelist) n_f = len(filelist)
for i, f in enumerate(filelist): for i, f in enumerate(filelist):
print(f"{i+1}/{n_f} {self.opts.command}ing {f} ", file=sys.stderr) 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): def set_phrase(self, twice=False):
if not self.phrase is None:
# phrase already set
return
self.phrase = os.environ.get("GPGPASS", None) self.phrase = os.environ.get("GPGPASS", None)
if self.phrase is 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() self.phrase = getpass()
if twice: if twice and len(self.phrase) > 0:
print("Type private key passphrase again to confirm") print("Type passphrase again to confirm")
phrase2 = getpass() phrase2 = getpass()
if phrase2 != self.phrase: if phrase2 != self.phrase:
print("Passphrases do not match!") print("Passphrases do not match!")
sys.exit(1) sys.exit(1)
if self.phrase == "" and self.symmetric:
print("Symmetric passphrase required!")
sys.exit(1)
def get_filelist(self, root, recurse, direction): def get_filelist(self, root, recurse, direction):
if not recurse: if not recurse:
@@ -229,9 +301,12 @@ class Processor:
return filelist return filelist
def keygen(self): def keygen(self):
if os.path.exists(self.opts.key): if not self.opts.force:
print(f"File {self.opts.key} already exists") for f in (self.opts.key, self.opts.key + ".pub"):
if os.path.exists(f):
print(f"File {f} already exists")
sys.exit(1) sys.exit(1)
print(f"Generating keys for {self.opts.name}...", file=sys.stderr) print(f"Generating keys for {self.opts.name}...", file=sys.stderr)
self.set_phrase(twice=True) self.set_phrase(twice=True)
input_data = self.gpg.gen_key_input( input_data = self.gpg.gen_key_input(
@@ -264,6 +339,8 @@ class Processor:
os.makedirs(os.path.dirname(out_file), exist_ok=True) os.makedirs(os.path.dirname(out_file), exist_ok=True)
else: else:
out_file = self.opts.out_path if self.opts.out_path else auto_path out_file = self.opts.out_path if self.opts.out_path else auto_path
if not self.opts.force:
if os.path.exists(out_file): if os.path.exists(out_file):
print(f"{out_file} already exists", file=sys.stderr) print(f"{out_file} already exists", file=sys.stderr)
return return
@@ -274,6 +351,11 @@ class Processor:
try: try:
with open(in_file, "rb") as fp: with open(in_file, "rb") as fp:
if self.opts.command == "encrypt": if self.opts.command == "encrypt":
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) d = self.gpg.encrypt_file(fp, self.uid, always_trust=True, armor=False)
if self.opts.command == "decrypt": if self.opts.command == "decrypt":
d = self.gpg.decrypt_file(fp, passphrase=self.phrase, always_trust=True) d = self.gpg.decrypt_file(fp, passphrase=self.phrase, always_trust=True)