merge
This commit is contained in:
106
qgpg/__init__.py
106
qgpg/__init__.py
@@ -8,6 +8,7 @@ import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import time
|
||||
from datetime import datetime
|
||||
from getpass import getpass
|
||||
|
||||
try:
|
||||
@@ -123,6 +124,8 @@ Usage
|
||||
GPGPASS=mysecretpassword qgpg --key ./mykey --decrypt file.gpg file.txt
|
||||
- Symmetric encryption (Just leave out the --key):
|
||||
qgpg --encrypt file.txt file.gpg
|
||||
- Check who owns the private key:
|
||||
qgpg --info --key ./mykey
|
||||
|
||||
""",
|
||||
)
|
||||
@@ -149,21 +152,20 @@ Usage
|
||||
action="store_true",
|
||||
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.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",
|
||||
@@ -177,6 +179,19 @@ Usage
|
||||
action="store_true",
|
||||
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.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")
|
||||
@@ -191,17 +206,22 @@ Usage
|
||||
)
|
||||
|
||||
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]:
|
||||
parsed.command = cmd[0]
|
||||
if parsed.command == "keygen":
|
||||
|
||||
if parsed.command in ("keygen", "info"):
|
||||
if parsed.key is None:
|
||||
parser.error("--key required for keygen")
|
||||
parser.error(f"--key required for {parsed.command}")
|
||||
|
||||
return parsed
|
||||
|
||||
|
||||
class Collector:
|
||||
"""Data collector for file streaming. Prints progress"""
|
||||
|
||||
def __init__(self, filename, width, bufcount):
|
||||
|
||||
self.progress = MiniProgress(bufcount, width)
|
||||
@@ -217,6 +237,8 @@ class Collector:
|
||||
|
||||
|
||||
class Processor:
|
||||
"""Main processor for the program"""
|
||||
|
||||
def __init__(self):
|
||||
self.opts = get_opts()
|
||||
self.homedir = tempfile.TemporaryDirectory()
|
||||
@@ -230,6 +252,10 @@ class Processor:
|
||||
self.keygen()
|
||||
filelist = []
|
||||
|
||||
if self.opts.command == "info":
|
||||
self.key_info()
|
||||
filelist = []
|
||||
|
||||
if self.opts.command in ("encrypt", "decrypt"):
|
||||
if not self.symmetric:
|
||||
import_result = self.gpg.import_keys_file(self.opts.key)
|
||||
@@ -257,7 +283,7 @@ class Processor:
|
||||
self.homedir.cleanup()
|
||||
|
||||
def set_phrase(self, twice=False):
|
||||
|
||||
"""Sets self.phrase. if `twice` asks user for phrase twice."""
|
||||
if not self.phrase is None:
|
||||
# phrase already set
|
||||
return
|
||||
@@ -281,6 +307,7 @@ class Processor:
|
||||
sys.exit(1)
|
||||
|
||||
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 os.path.isfile(root):
|
||||
return [root]
|
||||
@@ -301,6 +328,7 @@ class Processor:
|
||||
return filelist
|
||||
|
||||
def keygen(self):
|
||||
"""Main key generator function"""
|
||||
if not self.opts.force:
|
||||
for f in (self.opts.key, self.opts.key + ".pub"):
|
||||
if os.path.exists(f):
|
||||
@@ -328,7 +356,53 @@ class Processor:
|
||||
os.chmod(self.opts.key, 0o600)
|
||||
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):
|
||||
"""encrypt or decrypt single file"""
|
||||
|
||||
if self.opts.command == "encrypt":
|
||||
auto_path = in_file + self.suffix
|
||||
if self.opts.command == "decrypt":
|
||||
@@ -371,11 +445,17 @@ class Processor:
|
||||
|
||||
|
||||
def strip_prefix(s, prefix):
|
||||
"""Return string without a prefix"""
|
||||
if s.startswith(prefix):
|
||||
return s[len(prefix) :]
|
||||
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():
|
||||
Processor()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user