merge
This commit is contained in:
106
qgpg/__init__.py
106
qgpg/__init__.py
@@ -8,6 +8,7 @@ import shutil
|
|||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import time
|
import time
|
||||||
|
from datetime import datetime
|
||||||
from getpass import getpass
|
from getpass import getpass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -123,6 +124,8 @@ Usage
|
|||||||
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):
|
- Symmetric encryption (Just leave out the --key):
|
||||||
qgpg --encrypt file.txt file.gpg
|
qgpg --encrypt file.txt file.gpg
|
||||||
|
- Check who owns the private key:
|
||||||
|
qgpg --info --key ./mykey
|
||||||
|
|
||||||
""",
|
""",
|
||||||
)
|
)
|
||||||
@@ -149,21 +152,20 @@ Usage
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Decrypt files",
|
help="Decrypt files",
|
||||||
)
|
)
|
||||||
|
commands_grp.add_argument(
|
||||||
|
"--info",
|
||||||
|
"-i",
|
||||||
|
default=False,
|
||||||
|
action="store_true",
|
||||||
|
help="Show key info",
|
||||||
|
)
|
||||||
crypt_grp = parser.add_argument_group("Encryption", "Encryption and Decryption options")
|
crypt_grp = parser.add_argument_group("Encryption", "Encryption and Decryption options")
|
||||||
|
|
||||||
crypt_grp.add_argument(
|
crypt_grp.add_argument(
|
||||||
"--key",
|
"--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.",
|
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(
|
crypt_grp.add_argument(
|
||||||
"--recursive",
|
"--recursive",
|
||||||
"-r",
|
"-r",
|
||||||
@@ -177,6 +179,19 @@ Usage
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Overwrite existing files",
|
help="Overwrite existing files",
|
||||||
)
|
)
|
||||||
|
keygen_grp = parser.add_argument_group(
|
||||||
|
"Keygen", "Options for keygen. Note: --key is required for keygen, see Encryption."
|
||||||
|
)
|
||||||
|
keygen_grp.add_argument(
|
||||||
|
"--name",
|
||||||
|
help="Name of the owner of the key (usually email) Default: %(default)s",
|
||||||
|
required=False,
|
||||||
|
default="recipient@address",
|
||||||
|
)
|
||||||
|
info_grp = parser.add_argument_group(
|
||||||
|
"Info", "Options for info. Note: --key is required for info. Give the private key file name."
|
||||||
|
)
|
||||||
|
|
||||||
misc_grp = parser.add_argument_group("Misc", "Other options")
|
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("--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("--no-progress", help="Disable progress meter.", default=False, action="store_true")
|
||||||
@@ -191,17 +206,22 @@ Usage
|
|||||||
)
|
)
|
||||||
|
|
||||||
parsed = parser.parse_args()
|
parsed = parser.parse_args()
|
||||||
for cmd in zip(("keygen", "encrypt", "decrypt"), (parsed.keygen, parsed.encrypt, parsed.decrypt)):
|
for cmd in zip(
|
||||||
|
("keygen", "encrypt", "decrypt", "info"), (parsed.keygen, parsed.encrypt, parsed.decrypt, parsed.info)
|
||||||
|
):
|
||||||
if cmd[1]:
|
if cmd[1]:
|
||||||
parsed.command = cmd[0]
|
parsed.command = cmd[0]
|
||||||
if parsed.command == "keygen":
|
|
||||||
|
if parsed.command in ("keygen", "info"):
|
||||||
if parsed.key is None:
|
if parsed.key is None:
|
||||||
parser.error("--key required for keygen")
|
parser.error(f"--key required for {parsed.command}")
|
||||||
|
|
||||||
return parsed
|
return parsed
|
||||||
|
|
||||||
|
|
||||||
class Collector:
|
class Collector:
|
||||||
|
"""Data collector for file streaming. Prints progress"""
|
||||||
|
|
||||||
def __init__(self, filename, width, bufcount):
|
def __init__(self, filename, width, bufcount):
|
||||||
|
|
||||||
self.progress = MiniProgress(bufcount, width)
|
self.progress = MiniProgress(bufcount, width)
|
||||||
@@ -217,6 +237,8 @@ class Collector:
|
|||||||
|
|
||||||
|
|
||||||
class Processor:
|
class Processor:
|
||||||
|
"""Main processor for the program"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.opts = get_opts()
|
self.opts = get_opts()
|
||||||
self.homedir = tempfile.TemporaryDirectory()
|
self.homedir = tempfile.TemporaryDirectory()
|
||||||
@@ -230,6 +252,10 @@ class Processor:
|
|||||||
self.keygen()
|
self.keygen()
|
||||||
filelist = []
|
filelist = []
|
||||||
|
|
||||||
|
if self.opts.command == "info":
|
||||||
|
self.key_info()
|
||||||
|
filelist = []
|
||||||
|
|
||||||
if self.opts.command in ("encrypt", "decrypt"):
|
if self.opts.command in ("encrypt", "decrypt"):
|
||||||
if not self.symmetric:
|
if not self.symmetric:
|
||||||
import_result = self.gpg.import_keys_file(self.opts.key)
|
import_result = self.gpg.import_keys_file(self.opts.key)
|
||||||
@@ -257,7 +283,7 @@ class Processor:
|
|||||||
self.homedir.cleanup()
|
self.homedir.cleanup()
|
||||||
|
|
||||||
def set_phrase(self, twice=False):
|
def set_phrase(self, twice=False):
|
||||||
|
"""Sets self.phrase. if `twice` asks user for phrase twice."""
|
||||||
if not self.phrase is None:
|
if not self.phrase is None:
|
||||||
# phrase already set
|
# phrase already set
|
||||||
return
|
return
|
||||||
@@ -281,6 +307,7 @@ class Processor:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def get_filelist(self, root, recurse, direction):
|
def get_filelist(self, root, recurse, direction):
|
||||||
|
"""returns a file list: if encrypting, lists files without .gpg, if decrytping, lists files with .gpg"""
|
||||||
if not recurse:
|
if not recurse:
|
||||||
if os.path.isfile(root):
|
if os.path.isfile(root):
|
||||||
return [root]
|
return [root]
|
||||||
@@ -301,6 +328,7 @@ class Processor:
|
|||||||
return filelist
|
return filelist
|
||||||
|
|
||||||
def keygen(self):
|
def keygen(self):
|
||||||
|
"""Main key generator function"""
|
||||||
if not self.opts.force:
|
if not self.opts.force:
|
||||||
for f in (self.opts.key, self.opts.key + ".pub"):
|
for f in (self.opts.key, self.opts.key + ".pub"):
|
||||||
if os.path.exists(f):
|
if os.path.exists(f):
|
||||||
@@ -328,7 +356,53 @@ class Processor:
|
|||||||
os.chmod(self.opts.key, 0o600)
|
os.chmod(self.opts.key, 0o600)
|
||||||
print(f"Generated {self.opts.key} and {self.opts.key}.pub", file=sys.stderr)
|
print(f"Generated {self.opts.key} and {self.opts.key}.pub", file=sys.stderr)
|
||||||
|
|
||||||
|
def key_info(self):
|
||||||
|
"""Main key info printing function"""
|
||||||
|
|
||||||
|
def print_key(key):
|
||||||
|
|
||||||
|
key_type = "NA"
|
||||||
|
if key.get("type") == "sec":
|
||||||
|
key_type = "Private"
|
||||||
|
if key.get("type") == "pub":
|
||||||
|
key_type = "Public"
|
||||||
|
|
||||||
|
try:
|
||||||
|
key_date = datetime.fromtimestamp(int(key["date"])).isoformat()
|
||||||
|
except Exception as e:
|
||||||
|
print(e, file=sys.stderr)
|
||||||
|
key_date = "NA"
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"""Fingerprint: {key.get('fingerprint')}
|
||||||
|
KeyId: {' '.join(string_chunk(key.get('keyid',''),8))}
|
||||||
|
Name: {', '.join(key.get('uids'))}
|
||||||
|
Type: {key_type}
|
||||||
|
Date: {key_date}
|
||||||
|
KeyLength: {key.get('length')}"""
|
||||||
|
)
|
||||||
|
|
||||||
|
import_result = self.gpg.import_keys_file(self.opts.key)
|
||||||
|
if len(import_result.fingerprints) == 0:
|
||||||
|
self.homedir.cleanup()
|
||||||
|
print(f"File does not contain private or public GPG keys!", file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
keys = self.gpg.list_keys(True)
|
||||||
|
|
||||||
|
for key in keys:
|
||||||
|
# Private keys
|
||||||
|
print_key(key)
|
||||||
|
if len(keys) > 0:
|
||||||
|
# if private keys found, do not print public keys
|
||||||
|
return
|
||||||
|
for key in self.gpg.list_keys():
|
||||||
|
# only public keys in the list
|
||||||
|
print_key(key)
|
||||||
|
|
||||||
def process_single(self, in_file):
|
def process_single(self, in_file):
|
||||||
|
"""encrypt or decrypt single file"""
|
||||||
|
|
||||||
if self.opts.command == "encrypt":
|
if self.opts.command == "encrypt":
|
||||||
auto_path = in_file + self.suffix
|
auto_path = in_file + self.suffix
|
||||||
if self.opts.command == "decrypt":
|
if self.opts.command == "decrypt":
|
||||||
@@ -371,11 +445,17 @@ class Processor:
|
|||||||
|
|
||||||
|
|
||||||
def strip_prefix(s, prefix):
|
def strip_prefix(s, prefix):
|
||||||
|
"""Return string without a prefix"""
|
||||||
if s.startswith(prefix):
|
if s.startswith(prefix):
|
||||||
return s[len(prefix) :]
|
return s[len(prefix) :]
|
||||||
return s[:]
|
return s[:]
|
||||||
|
|
||||||
|
|
||||||
|
def string_chunk(string, length):
|
||||||
|
"""Split string in to even chunks"""
|
||||||
|
return (string[0 + i : length + i] for i in range(0, len(string), length))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
Processor()
|
Processor()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user