added symmetric encryption. changed options in to groups
This commit is contained in:
23
Bakefile
23
Bakefile
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
140
qgpg/__init__.py
140
qgpg/__init__.py
@@ -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)
|
||||||
|
|||||||
Reference in New Issue
Block a user